diff --git a/crates/no-mistakes/tests/support/docs_coverage_cli_helpers.rs b/crates/no-mistakes/tests/support/docs_coverage_cli_helpers.rs index 8b4db4145..8a656ff37 100644 --- a/crates/no-mistakes/tests/support/docs_coverage_cli_helpers.rs +++ b/crates/no-mistakes/tests/support/docs_coverage_cli_helpers.rs @@ -1,33 +1,21 @@ +use ignore::WalkBuilder; use std::collections::{BTreeSet, VecDeque}; use std::path::{Path, PathBuf}; -use std::process::Command; use syn::{GenericArgument, Item, Meta, PathArguments, Type}; pub(super) fn rust_sources(dir: &Path) -> Vec { - let repo = git_repo_root(dir); - let output = Command::new("git") - .args([ - "-C", - repo.to_str().expect("repository root must be UTF-8"), - "ls-files", - "--cached", - "--others", - "--exclude-standard", - "--", - "crates/no-mistakes/src", - ]) - .output() - .expect("git ls-files must be available for docs coverage"); - assert!( - output.status.success(), - "git ls-files failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - let mut paths = String::from_utf8(output.stdout) - .expect("git ls-files output must be UTF-8") - .lines() - .filter_map(|relative| { - let path = repo.join(relative); + let mut paths = WalkBuilder::new(dir) + .hidden(false) + .parents(false) + .git_global(false) + .git_exclude(false) + .require_git(false) + .build() + .map(|entry| { + entry.unwrap_or_else(|error| panic!("failed to inventory Rust sources: {error}")) + }) + .filter_map(|entry| { + let path = entry.into_path(); (path.extension().and_then(|ext| ext.to_str()) == Some("rs") && std::fs::symlink_metadata(&path) .map(|metadata| metadata.file_type().is_file()) @@ -39,28 +27,6 @@ pub(super) fn rust_sources(dir: &Path) -> Vec { paths } -fn git_repo_root(dir: &Path) -> PathBuf { - let output = Command::new("git") - .args([ - "-C", - dir.to_str().expect("source directory must be UTF-8"), - "rev-parse", - "--show-toplevel", - ]) - .output() - .expect("git rev-parse must be available for docs coverage"); - assert!( - output.status.success(), - "git rev-parse failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - PathBuf::from( - String::from_utf8(output.stdout) - .expect("git repository root must be UTF-8") - .trim(), - ) -} - pub(super) fn subcommand_enums(source: &str) -> Vec<(String, String)> { let file = syn::parse_file(source).expect("Rust sources must parse before extracting clap commands"); @@ -263,3 +229,37 @@ fn parses_inline_optional_subcommand_fixture() { vec![("OptionalArgs".to_string(), "OptionalCommand".to_string())] ); } + +#[test] +fn inventories_the_requested_source_tree_without_git_commands() { + let source = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../fixtures/docs-coverage/source-tree"); + let fixture = tempfile::tempdir().expect("create source inventory fixture"); + for entry in WalkBuilder::new(&source) + .hidden(false) + .ignore(false) + .git_ignore(false) + .git_global(false) + .git_exclude(false) + .require_git(false) + .build() + .map(Result::unwrap) + .filter(|entry| entry.path() != source) + { + let relative = entry.path().strip_prefix(&source).unwrap(); + let target = fixture.path().join(relative); + if entry + .file_type() + .is_some_and(|file_type| file_type.is_dir()) + { + std::fs::create_dir_all(&target).unwrap(); + } else { + std::fs::create_dir_all(target.parent().unwrap()).unwrap(); + std::fs::copy(entry.path(), &target).unwrap(); + } + } + assert_eq!( + rust_sources(fixture.path()), + vec![fixture.path().join("nested/command.rs")] + ); +} diff --git a/fixtures/docs-coverage/source-tree/.gitignore b/fixtures/docs-coverage/source-tree/.gitignore new file mode 100644 index 000000000..9389ffe06 --- /dev/null +++ b/fixtures/docs-coverage/source-tree/.gitignore @@ -0,0 +1 @@ +ignored.rs diff --git a/fixtures/docs-coverage/source-tree/README.md b/fixtures/docs-coverage/source-tree/README.md new file mode 100644 index 000000000..f7ab86548 --- /dev/null +++ b/fixtures/docs-coverage/source-tree/README.md @@ -0,0 +1,3 @@ +# Docs coverage source-tree fixture + +The Rust source inventory must remain scoped to this tree and work without Git metadata. diff --git a/fixtures/docs-coverage/source-tree/nested/command.rs b/fixtures/docs-coverage/source-tree/nested/command.rs new file mode 100644 index 000000000..67174b1df --- /dev/null +++ b/fixtures/docs-coverage/source-tree/nested/command.rs @@ -0,0 +1,3 @@ +pub enum FixtureCommand { + Check, +} diff --git a/fixtures/docs-coverage/source-tree/nested/ignored.rs b/fixtures/docs-coverage/source-tree/nested/ignored.rs new file mode 100644 index 000000000..68a828d7a --- /dev/null +++ b/fixtures/docs-coverage/source-tree/nested/ignored.rs @@ -0,0 +1 @@ +compile_error!("gitignored Rust sources must not enter docs coverage inventory");