Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,13 @@ jobs:
- name: Run coverage
run: |
source /tmp/nix-dev-env.sh
# hiroz-tests needs its own features: several suites are
# `#![cfg(feature = "ros-msgs")]` and compile to empty binaries without
# them, so coverage taken blind under-reports the paths they exercise.
cargo llvm-cov \
-p hiroz -p hiroz-codegen -p hiroz-cdr -p hiroz-protocol -p hiroz-schema \
-p hiroz-tests \
--features hiroz-tests/ros-msgs,hiroz-tests/jazzy \
-j4 \
--lcov --output-path lcov.info
shell: bash
Expand Down
28 changes: 28 additions & 0 deletions crates/hiroz-tests/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
use std::{env, path::PathBuf};

fn main() {
// Package-wide enforcement of the feature requirement.
//
// `tests/feature_gate.rs` fails loudly when the crate is built without
// `ros-msgs`, but only if Cargo happens to select *that* target. Ask for one
// gated suite on its own -- `cargo test -p hiroz-tests --test cache` with no
// features -- and Cargo builds only that target, the crate-level `cfg`
// compiles it to an empty binary, `0 passed` is reported, and the guard
// never runs. The silent-skip mode the guard exists to close is still
// reachable through the narrower invocation.
//
// A build script runs for every build of this package regardless of which
// targets are selected, so this is the only place the requirement can be
// stated once and hold everywhere.
//
// Consequence, already declared in `feature_gate.rs`: `hiroz-tests` has no
// supported featureless configuration.
if std::env::var_os("CARGO_FEATURE_ROS_MSGS").is_none() {
panic!(
"\n\nhiroz-tests requires the `ros-msgs` feature.\n\n\
Without it the suites gated on it compile to empty test binaries \n\
that report `0 passed`, which reads as green but is no coverage.\n\n\
Build it as:\n\n \
cargo test -p hiroz-tests --features ros-msgs,jazzy\n\n\
Suites that drive a real ROS 2 installation need \n \
--features ros-interop,<distro> instead.\n"
);
}

// Declare custom cfg for ROS version detection
println!("cargo::rustc-check-cfg=cfg(ros_humble)");

Expand Down
58 changes: 58 additions & 0 deletions crates/hiroz-tests/tests/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Fails loudly when `hiroz-tests` is built without the features its suites need.
//!
//! Ten suites in this crate are `#![cfg(feature = "ros-msgs")]` — `cache.rs`,
//! `lifecycle.rs`, `parameter_tests.rs`, `service_schema_discovery.rs`,
//! `subscriber_timeout.rs`, `type_description_integration.rs`, the `z_*_example`
//! suites, and others. A crate-level `cfg` that is not satisfied does not error
//! and does not warn: the file compiles to an empty test binary reporting
//! `0 passed`, which is indistinguishable from green in every runner and log.
//!
//! Two separate mechanisms could hide that, and they are worth keeping distinct:
//!
//! * **Selection.** `Cargo.toml` sets
//! `default-members = ["crates/hiroz", "crates/hiroz-codegen"]`, so a bare
//! `cargo nextest run` never selected `hiroz-tests` at all — the crate was not
//! built, rather than built empty. `scripts/test-pure-rust.nu` now names the
//! crate explicitly.
//! * **Empty compilation.** Ask for the crate without features and the gated
//! suites really do compile away to `0 passed`. That is what this file and the
//! build script guard against.
//!
//! This file is deliberately **not** gated, so it is the one thing in the crate
//! that a featureless build cannot compile away. If the features go missing
//! again, the run fails with a message naming the cause instead of quietly
//! shrinking.
//!
//! Consequence, stated so it is a decision rather than a surprise: `hiroz-tests`
//! has no supported featureless configuration. Run it as
//! `cargo test -p hiroz-tests --features ros-msgs,jazzy` (or with `ros-interop`
//! for the suites that also need a ROS installation).

/// Without `ros-msgs`, the gated suites are silently absent — fail instead.
#[test]
#[cfg(not(feature = "ros-msgs"))]
fn ros_msgs_gated_suites_must_not_be_silently_skipped() {
Comment thread
YuanYuYuan marked this conversation as resolved.
panic!(
"hiroz-tests was built without the `ros-msgs` feature.\n\
\n\
The suites gated on it — `cache.rs`, `lifecycle.rs`, \
`parameter_tests.rs` and others — have been \
compiled to empty test binaries and will report `0 passed`, which reads \
as green. That is not a pass; it is no coverage.\n\
\n\
Build the crate with its features:\n\
\n\
cargo test -p hiroz-tests --features ros-msgs,jazzy\n\
\n\
Suites that additionally drive a real ROS 2 installation need \
`--features ros-interop,<distro>` instead."
);
}

/// With `ros-msgs`, record that the gate was satisfied.
///
/// Present so the guard is visible in the test list in *both* configurations —
/// a check whose only evidence is the absence of a failure is not a check.
#[test]
#[cfg(feature = "ros-msgs")]
fn ros_msgs_gated_suites_are_compiled_in() {}
3 changes: 3 additions & 0 deletions crates/hiroz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ pub mod python_bridge;
pub mod qos;
/// Internal message queues.
pub mod queue;

/// Debug-time enforcement of "no user callback under a hiroz lock guard".
pub mod reentrancy;
/// Message type metadata traits (`WithTypeInfo`, etc.).
pub mod ros_msg;
/// ROS 2 service client and server.
Expand Down
Loading
Loading