Skip to content

Commit 53356ff

Browse files
committed
refactor(test): replace #[cfg] with #[cfg_attr(..., ignore)] for test skipping
Use `#[cfg_attr(..., ignore = "reason")]` instead of `#[cfg(not(...))]` on tests that can still compile under the skipped condition. This keeps the tests compiled on all configurations, catching type errors early while only skipping execution at runtime. Changes: - cli_errors::fs_errors: replace `#[cfg(not(pdu_test_skip_fs_errors))]` with `#[cfg_attr(pdu_test_skip_fs_errors, ignore)]`; remove the same gate from its imports and helper function - test_remove_overlapping_paths: remove `#[cfg(unix)]` from module declaration; add `#[cfg_attr(not(unix), ignore)]` to each test - Add "Conditional Test Skipping" section to CONTRIBUTING.md - Add guideline bullet to AI instruction templates https://claude.ai/code/session_0184ceZayyCwvK7PEF9iB7K3
1 parent 800e537 commit 53356ff

8 files changed

Lines changed: 43 additions & 7 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1414
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
17+
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
1718
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1819
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `RUSTFLAGS` and `--cfg pdu_test_skip_*`, follow the hint and rerun with the suggested flags.
1920
- **ALWAYS run the full test suite** (`FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`) before committing, regardless of how trivial the change seems — this includes documentation-only changes, comment edits, config changes, and refactors. The test suite checks formatting, linting, building, tests, and docs across multiple feature combinations; any type of change can break any of these checks.

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1414
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
17+
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
1718
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1819
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `RUSTFLAGS` and `--cfg pdu_test_skip_*`, follow the hint and rerun with the suggested flags.
1920
- **ALWAYS run the full test suite** (`FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`) before committing, regardless of how trivial the change seems — this includes documentation-only changes, comment edits, config changes, and refactors. The test suite checks formatting, linting, building, tests, and docs across multiple feature combinations; any type of change can break any of these checks.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1414
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
17+
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
1718
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1819
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `RUSTFLAGS` and `--cfg pdu_test_skip_*`, follow the hint and rerun with the suggested flags.
1920
- **ALWAYS run the full test suite** (`FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`) before committing, regardless of how trivial the change seems — this includes documentation-only changes, comment edits, config changes, and refactors. The test suite checks formatting, linting, building, tests, and docs across multiple feature combinations; any type of change can break any of these checks.

CONTRIBUTING.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,36 @@ pub enum RuntimeError {
213213
}
214214
```
215215

216+
### Conditional Test Skipping: `#[cfg]` vs `#[cfg_attr(..., ignore)]`
217+
218+
When a test cannot run under certain conditions (e.g., wrong platform, running as root), prefer `#[cfg_attr(..., ignore)]` over `#[cfg(...)]` to skip it. `#[ignore]` keeps the test compiled on all configurations, catching type errors and regressions early — the test is simply skipped at runtime.
219+
220+
Use `#[cfg]` on tests **only** when the code cannot compile under the condition — for example, when the test references types, functions, or trait methods that are gated behind `#[cfg]` and do not exist on other platforms or feature sets.
221+
222+
Always include a reason string in the `ignore` attribute to explain why the test is skipped.
223+
224+
```rust
225+
// Good — test compiles everywhere, skipped at runtime on non-unix
226+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
227+
#[test]
228+
fn unix_path_logic() { /* uses hardcoded unix paths but no unix-only types */ }
229+
230+
// Good — test CANNOT compile on non-unix (uses unix-only types)
231+
#[cfg(unix)]
232+
#[test]
233+
fn block_size() { /* uses GetBlockSize which only exists on unix */ }
234+
235+
// Good — test compiles with the flag, skipped at runtime
236+
#[cfg_attr(pdu_test_skip_fs_errors, ignore = "pdu_test_skip_fs_errors is set")]
237+
#[test]
238+
fn fs_errors() { /* ... */ }
239+
240+
// Bad — excludes the test from compilation entirely when it could still compile
241+
#[cfg(not(pdu_test_skip_fs_errors))]
242+
#[test]
243+
fn fs_errors() { /* ... */ }
244+
```
245+
216246
### Using `pipe-trait`
217247

218248
This codebase uses the [`pipe-trait`](https://docs.rs/pipe-trait) crate extensively. The `Pipe` trait enables method-chaining through unary functions, keeping code in a natural left-to-right reading order. Import it as `use pipe_trait::Pipe;`.

src/app/overlapping_arguments.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,5 @@ pub fn remove_items_from_vec_by_indices<Item>(vec: &mut Vec<Item>, indices: &Has
121121

122122
#[cfg(test)]
123123
mod test_remove_items_from_vec_by_indices;
124-
#[cfg(unix)]
125124
#[cfg(test)]
126125
mod test_remove_overlapping_paths;

src/app/overlapping_arguments/test_remove_overlapping_paths.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl Api for MockedApi {
6969
}
7070
}
7171

72+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
7273
#[test]
7374
fn remove_nothing() {
7475
let original = vec!["foo", "bar", "abc/def", "0/1/2"];
@@ -78,6 +79,7 @@ fn remove_nothing() {
7879
assert_eq!(actual, expected);
7980
}
8081

82+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
8183
#[test]
8284
fn remove_duplicated_arguments() {
8385
let original = dbg!(vec![
@@ -109,6 +111,7 @@ fn remove_duplicated_arguments() {
109111
assert_eq!(actual, expected);
110112
}
111113

114+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
112115
#[test]
113116
fn remove_overlapping_sub_paths() {
114117
let original = vec![
@@ -126,6 +129,7 @@ fn remove_overlapping_sub_paths() {
126129
assert_eq!(actual, expected);
127130
}
128131

132+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
129133
#[test]
130134
fn remove_all_except_current_dir() {
131135
let original = dbg!(vec!["foo", "bar", ".", "abc/def", "0/1/2"]);
@@ -161,6 +165,7 @@ fn remove_all_except_current_dir() {
161165
assert_eq!(actual, expected);
162166
}
163167

168+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
164169
#[test]
165170
fn remove_all_except_parent_dir() {
166171
let original = dbg!(vec!["foo", "bar", "..", "abc/def", ".", "0/1/2"]);
@@ -184,6 +189,7 @@ fn remove_all_except_parent_dir() {
184189
assert_eq!(actual, expected);
185190
}
186191

192+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
187193
#[test]
188194
fn remove_overlapping_real_paths() {
189195
let original = dbg!(vec![
@@ -226,6 +232,7 @@ fn remove_overlapping_real_paths() {
226232
assert_eq!(actual, expected);
227233
}
228234

235+
#[cfg_attr(not(unix), ignore = "test uses hardcoded unix paths")]
229236
#[test]
230237
fn do_not_remove_symlinks() {
231238
let original = dbg!(vec![

template/ai-instructions/shared.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1414
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
17+
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
1718
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1819
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `RUSTFLAGS` and `--cfg pdu_test_skip_*`, follow the hint and rerun with the suggested flags.
1920
- **ALWAYS run the full test suite** (`FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh`) before committing, regardless of how trivial the change seems — this includes documentation-only changes, comment edits, config changes, and refactors. The test suite checks formatting, linting, building, tests, and docs across multiple feature combinations; any type of change can break any of these checks.

tests/cli_errors.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ use std::process::{Command, Output, Stdio};
1010
use text_block_macros::text_block;
1111

1212
#[cfg(unix)]
13-
#[cfg(not(pdu_test_skip_fs_errors))]
1413
use maplit::btreeset;
1514
#[cfg(unix)]
16-
#[cfg(not(pdu_test_skip_fs_errors))]
1715
use parallel_disk_usage::{
1816
bytes_format::BytesFormat,
1917
data_tree::DataTree,
@@ -25,7 +23,6 @@ use parallel_disk_usage::{
2523
visualizer::{BarAlignment, ColumnWidthDistribution, Direction, Visualizer},
2624
};
2725
#[cfg(unix)]
28-
#[cfg(not(pdu_test_skip_fs_errors))]
2926
use std::{collections::BTreeSet, path::Path};
3027

3128
fn stdio(command: Command) -> Command {
@@ -36,7 +33,6 @@ fn stdio(command: Command) -> Command {
3633
}
3734

3835
#[cfg(unix)]
39-
#[cfg(not(pdu_test_skip_fs_errors))]
4036
fn fs_permission(path: impl AsRef<Path>, permission: &'static str, recursive: bool) {
4137
let Output { status, stderr, .. } = Command::new("chmod")
4238
.pipe(|cmd| if recursive { cmd.with_arg("-R") } else { cmd })
@@ -110,14 +106,14 @@ fn max_depth_0() {
110106
}
111107

112108
#[cfg(unix)]
113-
#[cfg(not(pdu_test_skip_fs_errors))]
109+
#[cfg_attr(pdu_test_skip_fs_errors, ignore = "pdu_test_skip_fs_errors is set")]
114110
#[test]
115111
fn fs_errors() {
116112
if unsafe { libc::geteuid() } == 0 {
117113
panic!(
118114
"{}\n{}",
119115
"error: This test must not be run as root because running with elevated privileges would affect its accuracy.",
120-
"hint: Either run this test as a non-root user or set `RUSTFLAGS='--cfg pdu_test_skip_fs_errors'` to skip this test.",
116+
"hint: Either run this test as a non-root user or set `RUSTFLAGS='--cfg pdu_test_skip_fs_errors'` to ignore this test.",
121117
);
122118
}
123119

0 commit comments

Comments
 (0)