diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ac9c5c09..04f9ef45 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -16,6 +16,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. +- A unit-test module may sit inline as `mod tests { ... }` when it is short. Once it grows long enough to noticeably extend the parent, move it to an external file and declare the module with `#[cfg(test)] mod tests;` at the end of the parent. The external file lives at `src/foo/tests.rs` for a parent `src/foo.rs`, and at `src/foo/bar/tests.rs` for a parent `src/foo/bar.rs`. Use this layout even when the parent has no other submodules. - Install the toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`. - When you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, the help text files, `USAGE.md`, and the man page. **Do not attempt to regenerate these files manually.** Always use the script. - Validate changes with `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`. When a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. When a sync test fails, read its error message and run the exact command it reports. diff --git a/AGENTS.md b/AGENTS.md index ac9c5c09..04f9ef45 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. +- A unit-test module may sit inline as `mod tests { ... }` when it is short. Once it grows long enough to noticeably extend the parent, move it to an external file and declare the module with `#[cfg(test)] mod tests;` at the end of the parent. The external file lives at `src/foo/tests.rs` for a parent `src/foo.rs`, and at `src/foo/bar/tests.rs` for a parent `src/foo/bar.rs`. Use this layout even when the parent has no other submodules. - Install the toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`. - When you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, the help text files, `USAGE.md`, and the man page. **Do not attempt to regenerate these files manually.** Always use the script. - Validate changes with `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`. When a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. When a sync test fails, read its error message and run the exact command it reports. diff --git a/CLAUDE.md b/CLAUDE.md index d9631060..cbc9cd8a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,6 +16,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. +- A unit-test module may sit inline as `mod tests { ... }` when it is short. Once it grows long enough to noticeably extend the parent, move it to an external file and declare the module with `#[cfg(test)] mod tests;` at the end of the parent. The external file lives at `src/foo/tests.rs` for a parent `src/foo.rs`, and at `src/foo/bar/tests.rs` for a parent `src/foo/bar.rs`. Use this layout even when the parent has no other submodules. - Install the toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`. - When you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, the help text files, `USAGE.md`, and the man page. **Do not attempt to regenerate these files manually.** Always use the script. - Validate changes with `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`. When a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. When a sync test fails, read its error message and run the exact command it reports. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32c979d8..fcfe06e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -346,6 +346,30 @@ ExitCode::from(match self { }) ``` +## Unit Tests + +A unit-test module may either sit inline as `mod tests { ... }` in its parent or live in a dedicated external `tests` submodule. Use the inline form for short test modules. Once the block becomes long enough to obscure the surrounding module, move the tests into an external file. + +### When the inline form is acceptable + +The inline form `mod tests { ... }` is acceptable on its own. Reserve it for modules whose entire test suite fits in a small number of lines, so the block does not noticeably extend the parent. Use the number of lines as the deciding factor. + +### Where the external file sits + +When the tests live externally, the parent declares them at the end of the file with the standard declaration: + +```rust +#[cfg(test)] +mod tests; +``` + +The external file itself sits in a directory named after the parent, using the same path regardless of whether the parent has any other submodules. Concretely: + +- For `src/foo.rs`, the tests file is `src/foo/tests.rs`. +- For `src/foo/bar.rs`, the tests file is `src/foo/bar/tests.rs`. + +Do not flatten the tests into a sibling file such as `src/foo_tests.rs`, and do not skip the intermediate directory when the parent currently has no other submodules. This mirrors the flat file pattern (`module.rs` rather than `module/mod.rs`) described under [Module Organization](#module-organization). + ## Setup Install the required Rust toolchain and components before running any checks: diff --git a/template/ai-instructions/shared.md b/template/ai-instructions/shared.md index ac9c5c09..04f9ef45 100644 --- a/template/ai-instructions/shared.md +++ b/template/ai-instructions/shared.md @@ -16,6 +16,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. - Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` when skipping tests. Use `#[cfg]` on tests only when the code cannot compile under the condition, such as when it references types or functions that do not exist on other platforms. +- A unit-test module may sit inline as `mod tests { ... }` when it is short. Once it grows long enough to noticeably extend the parent, move it to an external file and declare the module with `#[cfg(test)] mod tests;` at the end of the parent. The external file lives at `src/foo/tests.rs` for a parent `src/foo.rs`, and at `src/foo/bar/tests.rs` for a parent `src/foo/bar.rs`. Use this layout even when the parent has no other submodules. - Install the toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`. - When you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, the help text files, `USAGE.md`, and the man page. **Do not attempt to regenerate these files manually.** Always use the script. - Validate changes with `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`. When a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. When a sync test fails, read its error message and run the exact command it reports.