Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

TierWhat it covers命令Speed
FormatCode stylecargo fmt --all -- --check~5s
LintWarnings, correctnesscargo clippy --all ...~60s
UnitIndividual functionscargo test --all ...~90s
CLICommand parsing, validationcargo test -p adora-cli~5s
Integration通过环境变量进行节点 I/Ocargo test --test example-tests~30s
SmokeFull CLI lifecyclecargo test --test example-smoke -- --test-threads=1~3min
E2EMulti-dataflow scenarioscargo test --test ws-cli-e2e -- --ignored --test-threads=1~2min
Fault toleranceRestart policies, timeoutscargo test --test fault-tolerance-e2e~45s
TyposSpelling安装 typos-cli,然后运行 typos~2s

Tier Details

单元测试

单元测试使用 #[cfg(test)] 模块,与被测试的代码放在同一文件中。包含测试的主要 crate:

CrateTest countWhat’s tested
adora-arrow-convert~26Arrow 类型的往返转换
adora-cli~96命令解析、值解析器、日志过滤、JSON 解析、WebSocket 客户端、集群配置
adora-coordinator~24WS 控制面/守护进程面、健康检查、并发请求、构件存储、速率限制器、错误信息脱敏
adora-coordinator-store~10内存和 redb CRUD、模式版本管理、持久化
adora-core~8Dataflow descriptor validation
adora-daemon~2Shlex argument parsing
adora-node-api~10输入跟踪、服务/动作辅助函数(ID 生成、send_service_request/response)
adora-log-utils~11Log parsing utilities
adora-message~36通用类型、WS 协议、节点/数据 ID、元数据、认证令牌
ros2-bridge~30ROS2 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()
  • 每个子命令的解析(runupdownstartstoplistlogsbuildgraphnewstatusinspect toptopic list/hz/echonode 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:

  1. 构建并运行节点 crate(例如 rust-dataflow-example-node
  2. ADORA_TEST_WITH_INPUTS 设置为包含定时事件的 JSON 文件
  3. 设置 ADORA_TEST_NO_OUTPUT_TIME_OFFSET=1 以获得确定性输出
  4. 将 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_dataflowrust-dataflow/dataflow.yml30s
smoke_rust_dataflow_dynamicrust-dataflow/dataflow_dynamic.yml30s
smoke_rust_dataflow_socketrust-dataflow/dataflow_socket.yml30s
smoke_rust_dataflow_urlrust-dataflow-url/dataflow.yml30s
smoke_benchmarkbenchmark/dataflow.yml30s
smoke_log_sink_filelog-sink-file/dataflow.yml30s
smoke_log_sink_alertlog-sink-alert/dataflow.yml30s
smoke_log_sink_tcplog-sink-tcp/dataflow.yml30s
smoke_python_dataflowpython-dataflow/dataflow.yml30s
smoke_python_asyncpython-async/dataflow.yaml15s
smoke_python_drainpython-drain/dataflow.yaml15s
smoke_python_logpython-log/dataflow.yaml15s
smoke_python_loggingpython-logging/dataflow.yml15s
smoke_python_multiple_arrayspython-multiple-arrays/dataflow.yml15s
smoke_python_concurrent_rwpython-concurrent-rw/dataflow.yml15s
smoke_service_exampleservice-example/dataflow.yml30s
smoke_action_exampleaction-example/dataflow.yml30s

Local tests (9):

Test示例stop-after
smoke_local_python_dataflowpython-dataflow/dataflow.yml30s
smoke_local_python_asyncpython-async/dataflow.yaml10s
smoke_local_python_drainpython-drain/dataflow.yaml10s
smoke_local_python_logpython-log/dataflow.yaml10s
smoke_local_python_loggingpython-logging/dataflow.yml10s
smoke_local_python_multiple_arrayspython-multiple-arrays/dataflow.yml10s
smoke_local_python_concurrent_rwpython-concurrent-rw/dataflow.yml10s
smoke_local_service_exampleservice-example/dataflow.yml10s
smoke_local_action_exampleaction-example/dataflow.yml10s

需要特殊依赖项(摄像头、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)
JobRunnerWhat runs
fmtubuntu-latestcargo fmt --all -- --check
clippyubuntu-latestcargo clippy --all ... -- -D warnings
testubuntu-latestcargo test --all ...(排除 Python + adora-examples)
e2eubuntu-latestexample-tests、容错测试、冒烟测试、WS 端到端测试
typosubuntu-latestcrate-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 中的模式:

联网模式(测试协调器 + 守护进程):

  1. 使用 Once 守卫构建节点(避免每个测试都重新构建)
  2. 使用 adora down 清理残留进程
  3. 使用 adora up 启动集群
  4. 使用 adora start --detach 运行数据流
  5. 轮询 adora list --json 检查完成状态
  6. 使用 adora stop --alladora down 进行清理

本地模式(单进程,进程内协调器):

  1. 使用 Once 守卫构建 CLI
  2. 运行 adora run <yaml> --stop-after <duration>
  3. 断言退出码为成功

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 冒烟测试,请确保已安装 pyarrownumpy

端到端测试并行运行时失败

冒烟测试和被忽略的端到端测试必须单线程运行:

cargo test --test example-smoke -- --test-threads=1
cargo test --test ws-cli-e2e -- --ignored --test-threads=1

集成测试输出与预期不匹配

  1. 检查是否设置了 ADORA_TEST_NO_OUTPUT_TIME_OFFSET=1(时间偏移因机器而异)
  2. 如果节点行为有意更改,请重新生成基线:
    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 强制执行此检查)