Skip to content

Test infra rework: YAML-driven unit-test collection + integration tier#372

Open
JohnYanxinLiu wants to merge 13 commits into
developfrom
johnliu/test-infra-rework
Open

Test infra rework: YAML-driven unit-test collection + integration tier#372
JohnYanxinLiu wants to merge 13 commits into
developfrom
johnliu/test-infra-rework

Conversation

@JohnYanxinLiu

Copy link
Copy Markdown
Collaborator

What features did you add and/or bugs did you address?

  • Which GitHub issue does this address?

    No dedicated issue. Third PR in the real-robot deployment series, extracted from the test-harness rework tangled in Johnliu/optitrack emulation #367 so the generic parts land on their own and Johnliu/optitrack emulation #367 shrinks to OptiTrack-only content when it rebases.

    Stacked on l4t deployment fixes: make the Jetson profile build + boot on real hardware #371 (johnliu/l4t-deployment-fix) — base this PR on johnliu/l4t-deployment-fix so the diff shows only the 3 test-infra commits. After l4t deployment fixes: make the Jetson profile build + boot on real hardware #371 merges to develop, retarget to develop (the l4t commits drop out cleanly).

  • Additional description if not fully described in the GitHub issue

    Two concerns:

    1. Unit-test discovery: proxy files → YAML-driven direct collection

    • develop discovered co-located unit tests via hand-written proxy files under tests/robot/ (each ~20 lines of importlib + parents[N] path walks, and a second source of truth to keep in sync).
    • This replaces them with direct collection driven by the existing tests/colcon_unit_test_packages.yaml: conftest.py resolves each listed package to its test/ dir and injects the non-linter test_*.py files into collection under --import-mode=importlib, auto-marking each unit by path. ament lint files (test_copyright.py, etc.) are skipped — they run under colcon test.
    • Deleted the two proxy files. Adding a package's unit tests is now "list it in the YAML."

    2. Integration test tier

    • New tests/integration/ + integration mark + a shared robot_autonomy_stack fixture (robot-desktop container, no sim/GPU), slotted into _MODULE_ORDER between build_packages and the sim tiers.
  • Please add videos and images to demonstrate the feature.

    N/A — test-harness plumbing.

How did you implement it?

  • tests/pytest.ini: --import-mode=importlib (kills duplicate-basename collisions and the spec_from_file_location loader) + integration mark.
  • tests/conftest.py: repo_path/unit_test_files (glob robot/ros_ws/src/**/<pkg>/test, minus lint files), inject in pytest_configure (skipped when an explicit path is passed, so pytest tests/system/foo.py still narrows), auto-unit-mark in pytest_itemcollected, __integration__ ordering, and the robot_autonomy_stack fixture.
  • Explicit files (not dirs) are injected — pytest doesn't apply ignore rules to files recursed from an explicitly-passed dir, so passing the exact non-linter files is the only deterministic way to keep ament lint tests out.

How do you run and use it?

# Unit tests (hermetic, no Docker) — collected directly from package test/ dirs:
airstack test -m unit -v

# Integration tier (robot-desktop container, no sim/GPU):
airstack test -m integration -v

Adding a package's unit tests: list it under robot.packages in tests/colcon_unit_test_packages.yaml — no proxy file.

Testing with PyTest

  • This is test infrastructure. Validated locally: the co-located natnet_ros2 + lidar_point_cloud_filter unit tests collect directly (no proxy files) and pass — 14/14, matching the pre-change count; ament lint files excluded; collection order is unit → build_docker/build_packages → integration → sim tiers; an explicit path still narrows (no injection).
  • Command: airstack test -m unit -v (and -m integration -v skips cleanly when robot-desktop isn't built).
  • Expected: unit count unchanged vs develop; no import errors under --import-mode=importlib.

Documentation

  • Was mkdocs.yml updated? (y/n) n — no new page. Updated the add-unit-tests and run-system-tests skills, AGENTS.md, and docs/development/intermediate/testing/{index,unit_testing}.md to describe the YAML-driven mechanism (no proxies), plus a new tests/integration/README.md.

Versioning


🤖 Generated with Claude Code

JohnYanxinLiu and others added 4 commits July 21, 2026 12:58
…e resolution

Parametrize the robot-l4t compose service so a single service covers real
deployments without editing compose:
- AUTONOMY_ROLE and FCU_URL are now ${VAR:-default} overridable (and FCU_URL is
  unquoted so the literal serial path reaches mavros).
- Rosbag output path is BAG_STORAGE_PATH-overridable.

Update the configure-multi-robot skill to reflect the honor-pre-set-ROBOT_NAME
guard (#370): document pinning ROBOT_NAME in an override for a single real robot,
the never-on-the-shared-service caveat, and the unknown_robot fallback fixes by
topology.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deployment override for a single real PX4 robot on a Jetson (aarch64/l4t).
Surfaces the common knobs at the top with sensible defaults: ROBOT_NAME pinned
directly (single-robot shortcut honored by .bashrc), FCU_URL, AUTONOMY_ROLE,
BAG_STORAGE_PATH, and RECORD_BAGS. Mocap-agnostic — NatNet/external-vision
settings are added by a separate optitrack override.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two real-hardware build fixes for the Jetson profile:
- Dockerfile.l4t-stack-base: overwrite dustynv's /ros_entrypoint.sh with an
  `exec "$@"` passthrough. Its prebuilt source-ROS libs (fastcdr 2.2.5) were
  shadowing the apt Jazzy (2.2.7) that Dockerfile.robot layers on, crashing
  apt-built nodes like mavros with symbol-lookup errors under tmux autolaunch.
- zed/Dockerfile.zed-l4t: bump ZED SDK 4.2 -> 5.2 and move the coupled ROS deps
  together (zed_msgs 5.2.1, point_cloud_transport(_plugins) 4.x, add backward_ros).

Document both gotchas in the docker-build-profiles skill, and correct the stale
unknown-robot -> unknown_robot in the robot_identity reference doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Version-increment gate: bump above develop's 0.19.0-alpha.6 and record the l4t
deployment changes in the changelog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JohnYanxinLiu
JohnYanxinLiu force-pushed the johnliu/test-infra-rework branch from 1bc4dbd to d42dc4f Compare July 22, 2026 15:30
@JohnYanxinLiu
JohnYanxinLiu changed the base branch from johnliu/l4t-deployment-fix to develop July 22, 2026 19:15
JohnYanxinLiu and others added 5 commits July 22, 2026 15:32
…egration tier

Unit tests are defined by tests/colcon_unit_test_packages.yaml: conftest.py resolves
each listed package to its <pkg>/test dir and collects the non-linter test_*.py files
under --import-mode=importlib (set in pytest.ini), marking each `unit` by path. ament
lint files are skipped (they run under colcon test). Removes two now-unnecessary files
under tests/robot/; the package test/ dirs are collected directly.

Also add an integration test tier: tests/integration/ + `integration` mark + a shared
robot_autonomy_stack fixture (robot-desktop container, no sim/GPU), slotted into
_MODULE_ORDER between build_packages and the sim tiers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ckage YAML

Update the add-unit-tests and run-system-tests skills, AGENTS.md, and the unit-testing
docs: adding a unit test is "list the package in colcon_unit_test_packages.yaml", and the
source lives in the package's own test/ dir. Document the `integration` mark/tier.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Version-increment gate: bump above develop (0.19.0-alpha.6); alpha.7 is taken by the
l4t-deployment-fix PR. Record the test-infra changes in the changelog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…/harness/

Begin modularizing conftest.py (959 lines) by concern. Extract two self-contained
pieces into a new tests/harness/ package:
- harness/session.py: session-scoped mutable state (results dir, current pytest item,
  last subprocess output, logger) with setter/getter accessors. Hooks write it; helpers
  read it, so helper modules no longer reach into conftest globals.
- harness/discovery.py: unit-test discovery driven by colcon_unit_test_packages.yaml
  (repo_path, load_colcon_unit_test_config, colcon_test_robot_command, unit_test_dirs,
  unit_test_files, _is_unit_item).

conftest.py imports from harness and its hooks delegate to the session accessors; it
re-exports AIRSTACK_ROOT / colcon_test_robot_command / load_colcon_unit_test_config /
logger so existing `from conftest import ...` in the system tests keeps working
unchanged. Behavior-preserving (host-validated): `-m unit` still 14 passed / 152
deselected, 166 collected, same order. Follow-on: the commands/containers/metrics/sim
helpers and collection ordering move out the same way.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… tests/harness/

Continue modularizing conftest.py. Move the subprocess/ros2 command helpers
(harness/commands.py), docker container + compute-usage + image helpers
(harness/containers.py), MetricsRecorder + get_metrics/current_test_id
(harness/metrics.py), and the sim target configs + ros2 topic sampling
(harness/sim.py) out of conftest.py.

conftest.py drops from 836 to 360 lines and re-exports the harness helper API
(`from harness import *`) so `from conftest import <name>` in the system tests +
sensor_probes keeps working unchanged. Behavior-preserving: -m unit still 14 passed /
152 deselected, 166 collected, same order. Remaining in conftest: pytest hooks,
collection ordering, and the airstack_env / robot_autonomy_stack fixtures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JohnYanxinLiu
JohnYanxinLiu force-pushed the johnliu/test-infra-rework branch from 028d013 to d6e2e56 Compare July 22, 2026 19:34
JohnYanxinLiu and others added 2 commits July 22, 2026 16:58
…ction.py

Final step of the conftest.py modularization: move test ordering — _MODULE_ORDER, the
per-module phase chains, _module_key, and the parametrize-id rewrite — into
harness/collection.py. conftest's pytest_collection_modifyitems hook now delegates to
collection.modify_items(items).

conftest.py is now 246 lines (from 959): pytest hooks + the airstack_env /
robot_autonomy_stack fixtures. All helpers live in tests/harness/ by concern (session,
discovery, commands, containers, metrics, sim, collection). Behavior-preserving: -m unit
still 14 passed / 152 deselected, 166 collected, unit → build → integration → sim order
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Also sync docs/skills to the tests/harness/ layout (AGENTS.md, tests/README.md,
tests/integration/README.md, run-system-tests + add-unit-tests skills, unit_testing +
end_to_end_testing docs): helpers, MetricsRecorder, the workspace globs, and _MODULE_ORDER
now point at tests/harness/ instead of conftest.py (still re-exported via conftest).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@JohnYanxinLiu
JohnYanxinLiu force-pushed the johnliu/test-infra-rework branch from d6e2e56 to b732196 Compare July 22, 2026 20:58
@JohnYanxinLiu
JohnYanxinLiu marked this pull request as ready for review July 22, 2026 22:01
@JohnYanxinLiu
JohnYanxinLiu marked this pull request as draft July 22, 2026 22:09
@JohnYanxinLiu
JohnYanxinLiu force-pushed the johnliu/test-infra-rework branch from baa21bb to da6b57d Compare July 22, 2026 22:31
The builder-stage pip block pulled pytest >=8 transitively into /usr/local (copied
into the runtime image), shadowing Jazzy's apt python3-pytest 7.4. pytest 8 removed
the `path` argument from pytest_pycollect_makemodule, which apt's launch_pytest plugin
still declares — so every pytest invocation in the robot container aborted at plugin
registration. This broke `colcon test` for ament_python packages (e.g.
lidar_point_cloud_filter in test_colcon_test_robot), while ament_cmake gtest packages
were unaffected.

Pin pytest to Jazzy's version so the container is internally consistent and
launch_testing / launch_pytest remain usable for future launch-based tests. The test
runner (tests/docker) is a separate interpreter and keeps its newer pytest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JohnYanxinLiu
JohnYanxinLiu force-pushed the johnliu/test-infra-rework branch from da6b57d to 0b131d6 Compare July 22, 2026 22:32
@JohnYanxinLiu
JohnYanxinLiu marked this pull request as ready for review July 22, 2026 22:39
@JohnYanxinLiu
JohnYanxinLiu requested a review from krrishj18 July 22, 2026 22:39
…cates configures

The global ENV LD_LIBRARY_PATH puts isaac-sim's bundled libs
(.../isaacsim.ros2.bridge/jazzy/lib) on the linker path. Its older libcrypto.so.3
shadows the system one, so when the updated ca-certificates (20240203 →
20260601~24.04.1) runs its postinst `openssl`, it fails with
`version 'OPENSSL_3.0.9' not found`, aborting the apt transaction and failing the
isaac-sim image build (PX4 Tools/setup/ubuntu.sh, exit 100).

Clear LD_LIBRARY_PATH for that RUN only so apt/openssl use the system libcrypto;
the global ENV still applies to every other layer. Environmental break (new
ca-certificates × isaac-sim's stale bundled openssl) — not a code regression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant