Skip to content

nvx-rs: share the managed agent's cgroup and export test fixtures - #618

Merged
Pedro Henrique Penna (ppenna) merged 1 commit into
devfrom
nvx-rs-quality/agent-test-fixtures
Oct 20, 2026
Merged

Pedro Henrique Penna (ppenna) merged 1 commit into
devfrom
nvx-rs-quality/agent-test-fixtures

Conversation

@ppenna

Copy link
Copy Markdown
Contributor

Summary

The managed agent's unit tests (nvx::guest::managed_agent) built the same two fake trees in five places: a cgroup root with an empty nvx-exec cgroup, in cgroup.rs, in dispatch.rs, and inline in loopback.rs, and a host-filesystem export with the children 0 and 1, in dispatch.rs and mappings.rs. This change builds each tree once, in a constructor of TestSystem, the test seam that all of these tests already construct, so a change to the layout that the agent expects has one place to go and the tests that share a tree can no longer drift apart. Only test code changes.

Changes

All in nvx-rs/src/guest/managed_agent/:

  • seam.rs:
    • TestSystem::with_cgroup() returns a system whose cgroup_root is in a new temporary directory, with empty cgroup.procs and cgroup.subtree_control files and an nvx-exec cgroup whose pids.current reads 0 and whose cgroup.kill and cgroup.procs are empty.
    • TestSystem::with_host_export() returns a system whose hostfs_dir is in a new temporary directory and holds the export root, with the child 0, which holds the directory src and the file notes.txt, and the empty child 1.
    • Each returns the TempDir that keeps its tree alive, and leaves the other Paths at their defaults.
  • The sites that now share them:
    • with_cgroup: cgroup::tests::setup, removed, for its six tests; dispatch::tests::direct_system, removed, for its three tests; and loopback::tests::control_session_broker_and_real_agent_loop_back, which wrote the same five files inline and now adds its own export and spawner to the system that with_cgroup returns.
    • with_host_export: dispatch::tests::mapping_system, removed, for its four tests; and mappings::tests::setup, which still builds each test's HostMappings around it.
  • The Paths imports that only the copies used are gone.

The diff is 57 insertions and 96 deletions.

Behavior

Nothing observable changes. The change is confined to #[cfg(test)] code: the library, the guest binaries, and every public API are untouched.

  • Each fixture creates the same directories and files, with the same contents, at the same paths below its temporary directory, as each copy did. Only the loopback test's five files are now written in the order that the other copies used, which nothing observes.
  • The loopback test's system has the same paths and spawner: it set cgroup_root, hostfs_dir, and spawner with struct-update syntax, and now sets hostfs_dir and spawner on the system that with_cgroup returns. It still creates its own export, root/0/source, and its mapping target.
  • No test changes what it does or asserts, and each still holds its temporary directory for its whole run.

The guest module is Linux-only, so only Linux builds compile these tests. On Linux, cargo test --all-features runs every test that uses the fixtures, the loopback test included, and check-guest-binaries runs the cgroup, dispatch, and mappings tests in the guest profile (--no-default-features --features guest, for x86_64-unknown-linux-musl). No new test is needed: the fixtures are test code that these tests exercise.

Follow-ups

The survey turned these up, and this change leaves them alone. I found no suspected bug.

  • dispatch::tests::maps_payload and mappings::tests::payload are the same MAPS request encoder. Sharing it beside these fixtures would finish the managed agent's test fixtures, but mappings.rs calls it 19 times, so it wants a change of its own to stay small.
  • In guest/device_io.rs, run_file and run_network both measure, format the result line, and write it with the same error, which run could do once; and the device_io helper of tests/guest_tools.rs repeats the spawn and output collection of run.
  • In host/sandbox/mount.rs, MountAccess, MountOwner, and MountKind repeat from_name with the same message form, and parse_mount_value repeats the match of MountAccess::from_name with a message of its own; sandbox/mount/tests.rs and sandbox/tests.rs define the same policy test helper.
  • These follow-ups of earlier runs remain:
    • the option-values test helpers: option_values in host/filesystem.rs, rules in host/network/mod.rs, and values in host/sandbox/options/tests.rs;
    • Instance::runtime and Instance::launch in host/instance.rs, which read a record and refuse another STATE_FORMAT in two shapes;
    • the fixture of a Python process whose main thread exits first, in host/process.rs and host/cli/sandbox/identity.rs;
    • the args test helpers and the main entry convention of the guest programs, and the time tools' watcher-violation sites and port-window lock failures.

Validation

  • BASE: 3ddd1ea9e603be3a435b973c874adc474f08e904, origin/dev when the run started.
  • TREE: 71c4256a1da3b24b47b7e4fa67ac71a318f4d42e, the tree of this commit, applied as a patch on BASE on each host.
  • Release: v0.1.0-dev.3ddd1ea9e603, which nvx.py download installed on each host, built from BASE, with OpenVMM d0bdbed8f6e3b86271277dc22658109e2e633b2d, the pinned revision.
  • Guest artifacts: prometheus30 rebuilt the guest binaries from TREE with build-guest-binaries, ran test-guest-binaries on them (34 passed), and built the Alpine initramfs from them with NVX_GUEST_BINARIES. All three hosts ran the same initramfs.cpio.gz (SHA-256 54364e7301c11f7048f15c67f10c679f75d5ab5fc3cc1abd9025df6cef4b297e), package manifest, and provenance. Each host also rebuilt the nvx-rs CLI from TREE with cargo build --release and recorded its provenance before its hypervisor tests.
  • Local checks, on Windows: cargo fmt --check; Clippy with -D warnings for all targets and features and for the library without default features, and, because the guest module is Linux-only, cross-checked for x86_64-unknown-linux-gnu with all features and for x86_64-unknown-linux-musl with --no-default-features --features guest; cargo test with all features and with the defaults; and rustdoc with -D warnings. This machine also ran the steps for which a host lacks a toolchain or target: the Rust 1.89 MSRV check of both crates on Windows, and the aarch64-apple-darwin check of both crates, which CI runs on Linux.
Host profile Backend Host type check-nvx-rs check-aci-edge-sandboxes Guest binaries and initramfs test-aci-edge-sandboxes test-microvm (default suite)
prometheus32 kvm baremetal Linux steps pass, including MSRV 1.89, the bundled-release test, and check-guest-binaries; the macOS check ran locally Linux steps pass, including MSRV 1.89 and agent bundling; the macOS check ran locally — pass, 18 of 18 pass
prometheus30 mshv baremetal — — build-guest-binaries, test-guest-binaries, and build-initramfs pass pass, 18 of 18 pass
prometheus28 whp baremetal Windows steps pass, including the bundled-release test; MSRV ran locally Windows steps pass, including agent bundling; MSRV ran locally — pass, 18 of 18 pass

On prometheus32, the library tests of cargo test --all-features (548 passed) ran every test that uses the fixtures, the loopback test included, and those of check-guest-binaries (279 passed) the cgroup, dispatch, and mappings tests in the guest profile. directories_on_another_volume_are_mapped passes by skipping on hosts without a second volume, as on all three. No step failed or needed a rerun.

Left to CI: the test-microvm scenarios that run only when named (smp-lapic, managed-exec-config, and sandbox-lifecycle), the Ubuntu and Azure Linux guests, the OpenVMM suites, guest determinism, benchmarks, and check-quality and validate-nvx, which this change doesn't affect: it changes no Python, shell, or Markdown file, and adds, moves, or deletes none.

The managed agent's unit tests built the same fake trees in five
places. cgroup.rs's setup and dispatch.rs's direct_system were the same
function: a cgroup root with empty cgroup.procs and
cgroup.subtree_control files, and an nvx-exec cgroup whose pids.current
reads 0 and whose cgroup.kill and cgroup.procs are empty. The loopback
test wrote the same five files inline. dispatch.rs's mapping_system and
the setup of mappings.rs built the same host-filesystem export: child 0
with the directory src and the file notes.txt, and the empty child 1.

TestSystem, the seam that all of these tests construct, now builds both
trees: TestSystem::with_cgroup and TestSystem::with_host_export return
a system whose paths point into a new temporary directory, with the
directory that keeps it alive. The five copies become calls, so a
change to the layout that the agent expects has one place to go, and
the tests that share a tree can no longer drift apart.

Behavior is unchanged: only test code changes, and each fixture creates
the same directories and files, with the same contents, at the same
paths below its temporary directory, and leaves the other paths at
their defaults. The loopback test still adds its own export,
root/0/source, and its unprivileged spawner to the cgroup system. On
Linux, cargo test --all-features runs every test that uses the
fixtures, and check-guest-binaries runs the cgroup, dispatch, and
mappings tests in the guest profile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 04:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The test-only refactor preserves fixture contents and lifetimes, with no unresolved findings; tests were not rerun during review.

No open issues 🎉

What changed in this PR

Consolidates managed-agent test fixtures in TestSystem, reducing duplication without changing production code or test behavior.

Changes:

  • Adds shared cgroup and host-export fixture constructors.
  • Updates existing tests to use them while preserving temporary-directory lifetimes and test-specific setup.
File Description
nvx-rs/​src/​guest/​managed_agent/​seam.rs Adds both shared fixture constructors.
nvx-rs/​src/​guest/​managed_agent/​mappings.rs Reuses the host-export fixture.
nvx-rs/​src/​guest/​managed_agent/​loopback.rs Reuses the cgroup fixture, retaining custom setup.
nvx-rs/​src/​guest/​managed_agent/​dispatch.rs Replaces duplicated helpers with shared fixtures.
nvx-rs/​src/​guest/​managed_agent/​cgroup.rs Replaces local setup with the shared cgroup fixture.

Review effort: Balanced

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.

2 participants