Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
- 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
- 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)
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
- 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.
- **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.
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
- 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
- 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)
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
- 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.
- **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.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
- 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
- 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)
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
- 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.
- **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.
Expand Down
30 changes: 30 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,36 @@ pub enum RuntimeError {
}
```

### Conditional Test Skipping: `#[cfg]` vs `#[cfg_attr(..., ignore)]`

When a test cannot run under certain conditions (e.g., wrong platform, running as root), prefer `#[cfg_attr(..., ignore)]` over `#[cfg(...)]` to skip it. This way the test is still compiled on all configurations — catching type errors and regressions early — but simply skipped at runtime.

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.

Prefer including a reason string in the `ignore` attribute to explain why the test is skipped.

```rust
// Good — test compiles everywhere, skipped at runtime on non-unix
#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn unix_path_logic() { /* uses hardcoded unix paths but no unix-only types */ }

// Good — test CANNOT compile on non-unix (uses unix-only types)
#[cfg(unix)]
#[test]
fn block_size() { /* uses GetBlockSize which only exists on unix */ }

// Good — test compiles with the flag, skipped at runtime
#[test]
#[cfg_attr(pdu_test_skip_fs_errors, ignore = "pdu_test_skip_fs_errors is set")]
fn fs_errors() { /* ... */ }

// Bad — excludes the test from compilation entirely when it could still compile
#[cfg(not(pdu_test_skip_fs_errors))]
#[test]
fn fs_errors() { /* ... */ }
```

### Using `pipe-trait`

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;`.
Expand Down
1 change: 0 additions & 1 deletion src/app/overlapping_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,5 @@ pub fn remove_items_from_vec_by_indices<Item>(vec: &mut Vec<Item>, indices: &Has

#[cfg(test)]
mod test_remove_items_from_vec_by_indices;
#[cfg(unix)]
#[cfg(test)]
mod test_remove_overlapping_paths;
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ impl Api for MockedApi {
}

#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn remove_nothing() {
let original = vec!["foo", "bar", "abc/def", "0/1/2"];
let mut actual = original.clone();
Expand All @@ -79,6 +80,7 @@ fn remove_nothing() {
}

#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn remove_duplicated_arguments() {
let original = dbg!(vec![
"foo",
Expand Down Expand Up @@ -110,6 +112,7 @@ fn remove_duplicated_arguments() {
}

#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn remove_overlapping_sub_paths() {
let original = vec![
"foo/child",
Expand All @@ -127,6 +130,7 @@ fn remove_overlapping_sub_paths() {
}

#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn remove_all_except_current_dir() {
let original = dbg!(vec!["foo", "bar", ".", "abc/def", "0/1/2"]);
let mut actual = original.clone();
Expand Down Expand Up @@ -162,6 +166,7 @@ fn remove_all_except_current_dir() {
}

#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn remove_all_except_parent_dir() {
let original = dbg!(vec!["foo", "bar", "..", "abc/def", ".", "0/1/2"]);
let mut actual = original.clone();
Expand All @@ -185,6 +190,7 @@ fn remove_all_except_parent_dir() {
}

#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn remove_overlapping_real_paths() {
let original = dbg!(vec![
"foo",
Expand Down Expand Up @@ -227,6 +233,7 @@ fn remove_overlapping_real_paths() {
}

#[test]
#[cfg_attr(not(unix), ignore = "only one path separator style is tested")]
fn do_not_remove_symlinks() {
let original = dbg!(vec![
"foo",
Expand Down
1 change: 1 addition & 0 deletions template/ai-instructions/shared.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
- Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated
- 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
- 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)
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
- 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.
- **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.
6 changes: 1 addition & 5 deletions tests/cli_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ use std::process::{Command, Output, Stdio};
use text_block_macros::text_block;

#[cfg(unix)]
#[cfg(not(pdu_test_skip_fs_errors))]
use maplit::btreeset;
#[cfg(unix)]
#[cfg(not(pdu_test_skip_fs_errors))]
use parallel_disk_usage::{
bytes_format::BytesFormat,
data_tree::DataTree,
Expand All @@ -25,7 +23,6 @@ use parallel_disk_usage::{
visualizer::{BarAlignment, ColumnWidthDistribution, Direction, Visualizer},
};
#[cfg(unix)]
#[cfg(not(pdu_test_skip_fs_errors))]
use std::{collections::BTreeSet, path::Path};

fn stdio(command: Command) -> Command {
Expand All @@ -36,7 +33,6 @@ fn stdio(command: Command) -> Command {
}

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

#[cfg(unix)]
#[cfg(not(pdu_test_skip_fs_errors))]
#[test]
#[cfg_attr(pdu_test_skip_fs_errors, ignore = "pdu_test_skip_fs_errors is set")]
fn fs_errors() {
if unsafe { libc::geteuid() } == 0 {
panic!(
Expand Down
Loading