Testing guide
July 15, 2026 · View on GitHub
This guide describes where tests live, how to run them locally, and how they map to CI. For coding standards and PR workflow, see Contributing Guide. The canonical CI definition is .github/workflows/ci.yaml.
1. Test organization
Layout (simplified from the repository root):
Primus/
├── runner/
│ └── lib/
│ └── common.sh # Shared logging/helpers sourced by the shell test runner
├── tests/
│ ├── runner/ # Shell integration tests
│ │ ├── run_all_tests.sh # Master shell test runner
│ │ ├── lib/ # test_common.sh, test_config.sh, test_validation.sh
│ │ ├── helpers/ # Hook and env tests
│ │ ├── test_primus_cli.sh
│ │ ├── test_primus_cli_direct.sh
│ │ ├── test_primus_cli_container.sh
│ │ └── test_primus_cli_slurm.sh
│ ├── unit_tests/ # Python unit tests (pytest)
│ │ ├── agents/ # Tuning-agent tests
│ │ ├── backends/
│ │ ├── ci/
│ │ ├── cli/
│ │ ├── core/ # config, backend, launcher, patches, projection, pipeline_parallel, runtime, trainer, utils
│ │ ├── megatron/ # Megatron-specific unit tests
│ │ ├── modules/
│ │ └── tools/
│ ├── trainer/ # Integration tests (typically need GPU + data)
│ │ ├── test_megatron_trainer.py
│ │ ├── test_torchtitan_trainer.py
│ │ └── test_maxtext_trainer.py
│ ├── scripts/ # CI unit/integration launch scripts and UT patches
│ ├── utils.py # Shared test utilities
│ └── run_unit_tests.py # Optional orchestrator (walks tests/, see below)
tests/runner/run_all_tests.sh sources shared helpers from runner/lib/common.sh at the repository root (not under tests/runner/).
2. Running tests
Shell integration tests (CLI behavior, config loading, hooks, environment):
bash ./tests/runner/run_all_tests.sh
Python unit tests:
pytest tests/unit_tests/ --maxfail=1 -s
Trainer integration tests (GPU and data; may require Hugging Face access):
# Megatron
DATA_PATH=<path> pytest tests/trainer/test_megatron_trainer.py -s
# TorchTitan
DATA_PATH=<path> pytest tests/trainer/test_torchtitan_trainer.py -s
# MaxText (JAX) — often run via the orchestrator in CI
python ./tests/run_unit_tests.py --jax
tests/run_unit_tests.py walks tests/ and runs every test_*.py it finds, except for tests/trainer/test_maxtext_trainer.py in the default mode (that file is only selected when --jax is set). That means the default orchestrator run includes tests/unit_tests/ and tests/trainer/ (and any other matching tests), which is broader than pytest tests/unit_tests/ alone.
Orchestrator (default—all discovered tests except MaxText trainer):
python ./tests/run_unit_tests.py
Orchestrator (JAX / MaxText trainer only):
python ./tests/run_unit_tests.py --jax
3. Test types
- Shell tests: Exercise runner scripts, CLI wiring, configuration loading, hook execution, and environment setup. Implemented as bash scripts under
tests/runner/and orchestrated byrun_all_tests.sh. - Unit tests: Cover configuration parsing, preset loading, CLI behavior, patch registration, adapters, and other library logic under
tests/unit_tests/. - Trainer tests: End-to-end training against real backends; require AMD GPUs and appropriate data paths (and sometimes tokens). See
.github/workflows/ci.yamlfor CI values such asDATA_PATH,MASTER_PORT, andHSA_NO_SCRATCH_RECLAIM.
4. Writing new tests
- Pytest: Add files named
test_*.pyundertests/unit_tests/, following existing patterns and reusing fixtures fromconftest.pywhere present. - Shell: Add scripts under
tests/runner/or extendtests/runner/run_all_tests.shto invoke new suites, consistent with existingtest_primus_cli*.shscripts. - Backends: Prefer
tests/unit_tests/backends/<backend>/for adapter-focused tests.
5. CI pipeline details
From .github/workflows/ci.yaml:
code-lint: Python 3.12 on GitHub-hosted runners. Runspre-commit run --all-files --show-diff-on-failure, so checks follow.pre-commit-config.yaml.dependency-review: Runsactions/dependency-review-actionon pull requests to flag dependency changes.run-unittest-torch: Self-hosted GPU runner. Installsrequirements.txt, runsbash ./tests/runner/run_all_tests.sh, thenpytest tests/unit_tests/under coverage (--cov=primus --cov-report=term-missing) with specific tests--deselected (currently somemegatron/ccoTP-overlap andmegatron/transformer/moedispatcher cases—see the workflow file). Trainer steps setMASTER_PORT,DATA_PATH,HSA_NO_SCRATCH_RECLAIM=1, andHF_TOKENfor Megatron and TorchTitan trainer tests. A follow-up coverage step combines unit and E2E coverage.run-unittest-jax: JAX runner. Installsrequirements-jax.txt, runs the same shell test script, thenpython ./tests/run_unit_tests.py --jaxwith CI environment variables (for exampleJAX_SKIP_UT=1andDATA_PATHas defined in the workflow).
The build-docker job builds images after lint passes; unit test jobs depend on code-lint, not on build-docker.
6. Pre-commit hooks
Install once per clone:
pip install pre-commit
pre-commit install
Run manually on the whole tree:
pre-commit run --all-files
Hooks include: trailing-whitespace, end-of-file-fixer, check-yaml, check-added-large-files, check-merge-conflict, isort, autoflake, black, and shellcheck (as configured in .pre-commit-config.yaml). These align with the code-lint job in CI; see Contributing Guide for manual equivalents.