dynamo/tests/README.md

223 lines
8.1 KiB
Markdown

# Dynamo Testing Guidelines
This document provides instructions for organizing, marking, and running tests in the Dynamo project. Follow these guidelines to ensure consistency and maintainability across the test suite.
---
## Test Organization: Where to Store Tests
### Directory Structure
```
dynamo/
├── lib/
│ ├── runtime/
│ │ ├── src/
│ │ │ └── lib.rs # Rust code + unit tests inside
│ │ └── tests/ # Rust integration tests for runtime
│ ├── llm/
│ │ └── src/
│ │ └── lib.rs # Rust code + unit tests inside
│ │ └── tests/ # Rust integration tests for llm
│ └── ...
├── components/
│ ├── src/dynamo/
│ | └── planner/
│ | | └── zoo.py
│ | | └── foo/
│ | | | └── bar.py
│ │ │ └── tests/ # Python unit/integration tests for planner
│ │ │ | └── test_zoo.py
│ │ │ | └── foo/ # Mirror the source code file hierarchy
│ │ │ | | └── test_bar.py
│ | └── router/
│ │ │ └── tests/
│ | └── ...
│ ├── backend/
│ │ └── vllm
│ │ │ └── tests/ # Python unit/integration tests for backend
│ │ └── trtllm
│ │ │ └── tests/
│ │ └── trtllm
│ │ │ └── tests/
│ └── ...
├── tests/ # End-to-end and cross-component tests
│ ├── serve/
│ ├── kvbm/
│ ├── benchmark/
│ ├── fault_tolerance/
│ └── ...
```
- Place **unit/integration tests** for a component in its `tests/` subfolder.
- Place **end-to-end (E2E) tests** and cross-component tests in `dynamo/tests/`.
- Name test files as `test_<component>_<flow>.py` for clarity.
### Test Types and Locations
| Type | Description | Location |
|--------------|------------------------------------------|-----------------------|
| Unit | Single function/class, isolated | `<component>/tests/` |
| Integration | Interactions between modules/services | `<component>/tests/` |
| End-to-End | User workflows, CLI, API | `tests/serve/`, etc. |
| Benchmark | Performance/load | `tests/benchmark/` |
| Stress | Chaos, long-run, resource limits | `tests/fault_tolerance/` |
---
## Test Marking: How to Mark Tests
Markers are required for all tests. They are used for test selection in CI and local runs.
### Marker Requirements
- Every test must have at least one **Lifecycle** marker, and **test type** and **Hardware** markers.
- **component** markers are required as applicable.
### Marker Table
| Category | Marker(s) | Description |
|-------------------------|--------------------------|------------------------------------|
| Lifecycle [required] | pre_merge, post_merge, nightly, weekly, release | When the test should run |
| Test Type [required] | unit, integration, e2e, benchmark, stress, multimodal | Nature of the test |
| Hardware [required] | gpu_0, gpu_1, gpu_2, gpu_4, gpu_8, h100 | Number/type of GPUs required |
| Component/Framework | vllm, trtllm, sglang, kvbm, planner, router | Backend or component specificity |
| Execution | parallel | Test can run in parallel with pytest-xdist |
| Other | slow, skip, xfail, mypy, custom_build | Special handling |
### Example
```python
@pytest.mark.integration
@pytest.mark.gpu_2
@pytest.mark.vllm
def test_kv_cache_multi_gpu_behavior():
...
```
### Lifecycle Marker Note
Use the marker for the earliest pipeline stage where the test must run (e.g., `@pytest.mark.pre_merge`). This ensures the test is included in that stage and all subsequent ones (e.g., nightly, release), as CI pipelines select tests marked for earlier stages.
**Example:**
If a test is marked with `@pytest.mark.pre_merge`, and the nightly pipeline runs:
```bash
pytest -m "e2e and (pre_merge or post_merge or nightly)"
```
then this test will be included in the nightly run as well.
---
## Test Execution: How to Run Tests Locally and in CI
### Environment Setup
- Use the dev container for consistency.
- Install dependencies as specified in `pyproject.toml`.
- Set the `HF_TOKEN` environment variable for HuggingFace downloads:
```bash
export HF_TOKEN=your_token_here
```
- Model cache is located at `~/.cache/huggingface` to avoid repeated downloads.
### Running Tests
- Run all tests:
```bash
pytest
```
- Run by marker:
```bash
pytest -m "unit"
pytest -m "integration and gpu_1"
pytest -m "e2e and pre_merge"
pytest -m "benchmark and vllm"
```
- Run by component:
```bash
pytest -m planner
pytest -m kvbm
```
- Show print/log output:
```bash
pytest -s
```
- Run in container:
```bash
python container/render.py --framework=${FRAMEWORK} --target=runtime --output-short-filename
docker build -t dynamo:${FRAMEWORK}-latest -f container/rendered.Dockerfile .
./container/run.sh --mount-workspace -it -- pytest
./container/run.sh --mount-workspace -it -- pytest -m [optional markers]
```
- CI runs use the similar instructions as running inside the container. For example, running E2E tests as part of the nightly suite inside the Dynamo-VLLM container (which requires a single GPU) can be done with:
```bash
./container/run.sh --image $VLLM_IMAGE_NAME --name $VLLM_CONTAINER_NAME -- pytest -m "e2e and gpu_1 and (pre_merge or post_merge or nightly) "
```
#### Running tests locally outside of a container
To run tests outside of the development container, ensure that you have properly setup your environment and have installed the following dependencies in your `venv`:
```bash
uv pip install pytest-mypy
uv pip install pytest-asyncio
```
---
## Rust Testing: Organization and Execution
Rust tests in Dynamo are organized as follows:
- **Unit tests** are placed within the corresponding Rust source files (e.g., `lib.rs`) using `#[cfg(test)]` modules.
- **Integration tests** are placed in the crate's `tests/` directory and must be gated behind the `integration` feature.
### Test Segmentation by Features
- Use Cargo features to enable or disable groups of tests. For example:
```bash
cargo test --features planner
```
- Place all integration tests behind the `integration` feature gate. This ensures they are only run when explicitly enabled:
```bash
cargo test --features integration
cargo test --all-features
```
### Marking Slow or Special-Case Tests
- Use `#[ignore]` to mark slow or special-case tests. These tests will not run by default and must be explicitly included:
```bash
cargo test -- --ignored
```
### Example
```rust
#[cfg(test)]
mod kv_cache_tests {
#[test]
fn test_kv_cache_basic() {
// ...
}
#[test]
#[ignore]
fn test_kv_cache_long_running() {
// ...
}
}
```
### CI Integration
- CI runs integration tests using either `cargo test --features integration` or `cargo test --all-features`.
- Use feature gates to control which tests are included in each CI pipeline.
---
## Additional Requirements and Troubleshooting
- Tests must be deterministic; flaky tests are not permitted.
- Performance targets:
- Unit: <15 seconds per suite
- Integration: <5 minutes (premerge), <5 minutes (postmerge)
- E2E: <15 minutes (premerge).
- If a test is not running, verify the filename, markers, and folder location.
- For flaky tests, remove sources of randomness or set a fixed seed. Avoid unnecessary network calls.
- For slow tests, profile and optimize, or mark as `@pytest.mark.slow`.
- If model downloads fail, ensure `HF_TOKEN` is set and network access is available.
- If coverage is insufficient, add more tests or refactor code for better testability.
---
## References
- [pytest documentation](https://docs.pytest.org/en/stable/)
For further assistance, contact the Dynamo development team.