Repository navigation
feat(runner): add the test runner, plans and tmachine Session API, and run a plan on CI - #4773
matthewgrossman wants to merge 10 commits into
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
|
🌿 Preview your docs: https://nvidia-preview-pr-4773.docs.buildwithfern.com/openshell |
e06a0eb to
a23e140
Compare
be4af48 to
63f4141
Compare
63f4141 to
df44e02
Compare
df44e02 to
4d19336
Compare
4d19336 to
6c9a275
Compare
6c9a275 to
1da9c3b
Compare
1da9c3b to
4781a50
Compare
| let specs: Vec<_> = testkit_spec::scan(&source) | ||
| .map_err(|error| anyhow::anyhow!("{}: {error}", file.display()))? | ||
| .into_iter() | ||
| .filter(|spec| !spec.ignored) |
There was a problem hiding this comment.
The source catalog does not account for conditional attributes, so a healthy lane can fail JUnit verification. For example:
/// Disabled on every target.
#[cfg(any())]
#[testkit::test]
async fn disabled(cx: &mut Context) -> testkit::Result { /* ... */ }I reproduced the catalog including disabled and result verification reporting it missing, although Cargo correctly excludes it. Platform-specific #[cfg(...)] tests have the same problem, and #[cfg_attr(..., ignore)] is not recognized by the literal #[ignore] check either.
Could we resolve these attributes for the guest build, or explicitly reject unsupported conditional declarations with a clear error, rather than adding them to the expected test set? Reproduced on 1da9c3b832bdae01be7f4022bd9e030a84b2dbe4; the relevant catalog and parser behavior remains present at 4781a500c1f8e4064c1c64e8b37c963fa4a46902.
There was a problem hiding this comment.
Thanks, good catch. Rather than evaluate cfg for the guest target, testkit-spec now refuses it: a #[cfg] or #[cfg_attr] on a #[testkit::test] function, or a file-level #![cfg] in a file that has tests, is an error from both the macro and the catalog scan, so the catalog and the build can't disagree. A test that only applies to some environments belongs in a features or drivers suite, and #[ignore = "<issue>"] remains the way to skip one everywhere. Tests are in testkit-spec (rejects_conditional_tests, scan_rejects_conditional_files_with_tests), and tests/testkit/README.md documents the rule. Fixed in 1b80fad.
8e7e5d4 to
2b879b0
Compare
2b879b0 to
c4f944f
Compare
`#[testkit::test]` takes an optional `id = "t-0123abcd"` argument that gives a test a stable identity surviving renames and moves. The attribute's parsing moves into a new testkit-spec crate, which the test runner's catalog also uses to list tests from source without compiling them. The scanner refuses testkit tests inside inline modules. `#[should_panic]` stays rejected. There are no per-environment requirements: a test that cannot pass everywhere belongs in another family, and a broken one is `#[ignore]`d with an issue link. The testkit lockfile is also realigned with the root lockfile. Co-authored-by: Evan Lezar <elezar@nvidia.com> Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
Add a library API that boots an environment, runs commands in it with timeouts, copies files in and out, collects diagnostics and tears it down, so a caller can drive a machine without writing an Ansible playbook per suite. SSH and exec timeouts and QEMU cleanup on SIGTERM keep a hung guest or a cancelled CI job from leaving QEMU running. Add the `test-runner` flake package, which runs tests/runner with the toolchains and VM tools it needs. The Session API and test-runner package are adapted from #4629. Co-authored-by: Evan Lezar <elezar@nvidia.com> Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
…like the root `cargo --locked` only checks a lockfile against its own manifest. A workspace outside the root that builds root crates through path dependencies, such as the testkit suites building `openshell-sdk`, has its own Cargo.lock, so the two lockfiles can resolve a shared crate to different versions without any check failing. That is how aws-lc-sys 0.46 was picked up in #4629, which does not link against the AWS-LC that Nix provides. check_cargo_lockfile_alignment.py fails when an aligned workspace locks a registry crate that the root lockfile also contains at a version the root lockfile does not have. With --fix it re-locks the workspace starting from the root lockfile. tests/testkit is the first aligned workspace. The script and its tests come from #4629 (a37ebe9). Co-authored-by: Evan Lezar <elezar@nvidia.com> Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
Add the `sandbox` crate to the conformance workspace with one testkit test that creates a sandbox, runs `echo` in it with exec, deletes it and checks it is gone. It is deliberately small: it exists so a plan can run a real test through the runner on CI before the existing suites move to testkit. The workspace's other crate, the CLI conformance suite, keeps running as it does today. The conformance workspace joins the lockfile alignment check, so its lockfile is realigned with the root lockfile. The test is adapted from the `sandbox-lifecycle` suite in #4629. Co-authored-by: Evan Lezar <elezar@nvidia.com> Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
`openshell-test-runner` expands a plan (tests/plans/*.yaml) into one job per environment, builds the family's nextest archive for the guest, boots the environment through the tmachine Session API, runs nextest in it and brings back the JUnit report and logs. No per-suite Ansible playbook is involved. The runner points the tests at the system under test with environment variables: OPENSHELL_GATEWAY (the gateway the installer registered, from the environment's `gateway` field) and OPENSHELL_BIN (the installed CLI). Nextest runs with `--test-threads 1` and a slow-timeout that terminates a hung test. The JUnit report is checked against the catalog, so a missing or skipped expected test fails the job; `#[ignore]` tests are not expected and nextest's skipped case for them is accepted. `--strict` fails on an unsupported environment, a family with no tests, or an empty selection. `--emit-github-matrix` prints the GitHub Actions matrix for a plan, so the plan is the only list of what runs where. One environment is defined, amd64 Ubuntu with rootful Docker and the Debian package, which is the one CI can provision today; tests/plans/cpu-tests.yaml runs the conformance family on it. Further environments (Podman, k3s, arm64) are added with the suites that need them. There are no waivers or per-test requirements: the plan picks suites, and a broken test is `#[ignore]`d. Most of this comes from #4629, adapted to testkit finding the gateway the way the CLI does instead of reading a target file. Co-authored-by: Evan Lezar <elezar@nvidia.com> Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
Run fmt, clippy and nextest for tests/tmachine and tests/runner, and fmt and clippy for the new `sandbox` suite crate, wherever tests/testkit already runs them: the mise tasks and Branch Checks. The suite's tests need a gateway, so they run through `mise run e2e:testkit` against a local Docker gateway and, on CI, through the test runner rather than the unit-test job. The lockfile task and the Cargo lockfiles job also run the alignment check, and its own tests run with `mise run test`. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
Add a reusable workflow that takes a plan. A planning job runs `openshell-test-runner test --plan <plan> --emit-github-matrix`, and one job per environment, on the runner label the matrix gives, runs `nix run .#test-runner -- test --plan <plan> --env <id> --strict`. The jobs use the same integration inputs as the existing tmachine lanes, cache the tmachine disks keyed on the Ansible tree, upload the JUnit reports and logs whether or not the run passes, and an aggregator job reports the overall result. Branch E2E calls it for tests/plans/cpu-tests.yaml when the test:e2e label is set. The job is not in the Core E2E result's needs yet, so it starts out advisory. The existing conformance, feature and driver lanes are unchanged. Modeled on the E2E Suites workflow in #4629. Co-authored-by: Evan Lezar <elezar@nvidia.com> Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
Describe how a plan runs on CI and locally in TESTING.md and a new tests/plans/README.md, add a README for the sandbox suite, point the testkit README at the runner, and add rows for tests/suites, tests/tmachine, tests/runner, tests/plans and tests/environments to the project-structure tables in AGENTS.md and CONTRIBUTING.md. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
`mise run e2e:testkit` runs the conformance sandbox suite against a Docker-backed gateway started by e2e/with-docker-gateway.sh, the same way the runner runs it in a guest. It is the quick way to run the suite locally. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
…ependency Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
c4f944f to
be103d6
Compare
Summary
This PR adds a test runner that reads a plan (which test suites run in which environments), boots each environment as a VM, runs the suites there, and fails the lane if any expected test didn't pass. On CI, the
cpu-testsplan runs one smoke test on Ubuntu with rootful Docker. The plan is the only list of what runs where: the CI matrix is built from it.Most of the runner and the tmachine
SessionAPI come from @elezar's draft #4629.Stack
SessionAPI, optional test IDs andtestkit-spec, and a smoke suite on one Docker environment. Its CI lane is advisory.conformance-integrationlane onto the runner. Nothing is deleted.openshell-conformance, one program that runs the conformance suite against any gateway. The host e2e lanes move to it, and the old CLI conformance suite is deleted.featuresanddriverssuites (Keycloak providers, OCI image, Podman user namespaces) onto plans and deletescrates/openshell-conformance.Deferred from this PR
To later PRs in the stack:
plan-cpu-testslane is advisory and not part of the Core E2E result. test(suites): port the universal conformance suites onto testkit and run them from the plan #4774 movesconformance-integrationonto the runner.featuresanddriverssuites go into a plan.Dropped from #4629, not planned:
requires, capabilities and waivers. A test that fails in one environment fails the lane.#[ignore = "<issue link>"]takes a broken test out everywhere, and test(suites): port the universal conformance suites onto testkit and run them from the plan #4774 adds a check that conformance selects the same suites in every environment.catalogandmatrixsubcommands.--dry-runshows what a plan runs, and--emit-github-matrixcovers CI.OPENSHELL_GATEWAYandOPENSHELL_BIN.Out of scope for the whole stack: version compatibility guarantees between the conformance suite and gateways, and running tests from other repositories.
Related Issue
Part of #3954
Changes
tests/runner(openshell-test-runner): plan resolution, environments intests/environments, JUnit result checking (a missing, skipped, or unselected test fails the lane), strict mode, and--emit-github-matrix.--env,--suiteand--keepnarrow a local run and keep the VM for debugging. The catalog leaves#[ignore]tests out of what a lane expects, and refuses glob workspace members it can't expand.tests/plans/cpu-tests.yaml: the plan.tests/environments/docker.yamldefines the one amd64 Docker environment it uses.SessionAPI; SSH, scp, and exec timeouts;down()never panics; QEMU is cleaned up on SIGTERM; the SSH port isTMACHINE_SSH_PORT. Anopenshell-test-runnerflake package.#[testkit::test(id = "t-…")], and atestkit-speccrate that the macro and the runner's catalog share. It refuses tests inside inline modules and tests under#[cfg]/#[cfg_attr](or a file-level#![cfg]), because the catalog lists tests from source and can't tell whether they compile. A test without an ID is unchanged.tests/suites/conformance/sandbox: the smoke suite.mise run e2e:testkitruns it against a local Docker gateway..github/workflows/e2e-suites.yml(reusable) and aplan-cpu-testsjob in Branch E2E (labeltest:e2e): one job per environment from the plan, runningnix run .#openshell-test-runner -- test --plan ... --env <id> --strict, with JUnit and logs uploaded. It is not part of the Core E2E result yet.TESTING.md:#[ignore = "<issue link>"]is the only way to take a broken or flaky test out of a lane.Testing
--strictandOPENSHELL_TEST_STRICT.be103d680(run):plan-cpu-testspassed ontmachine.ubuntu2404-amd64.docker-rootful.debwithsmoke: create_exec_deleteselected from the plan.Checklist