mirror of
https://github.com/crate-ci/typos.git
synced 2026-10-17 01:25:12 +02:00
test(cli): Show panic when canonicalize fails mid-walk
Co-authored-by: Alexander Kireev <ak.chatman.media@gmail.com> Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
This commit is contained in:
co-authored by
Alexander Kireev
parent
d43b6c087a
commit
25761c7bf3
@@ -0,0 +1,82 @@
|
||||
//! Regression test for <https://github.com/crate-ci/typos/issues/1444>
|
||||
//!
|
||||
//! `walk_entry` canonicalizes each entry's path to look up its policy. When that fails,
|
||||
//! `report_result` reports the error and then hands back `PathBuf::default()`, an empty
|
||||
//! path that is never a key in `ConfigEngine`'s directory map, so the `policy()` call
|
||||
//! right after it panics with `` `walk()` should be called first ``.
|
||||
#![cfg(unix)]
|
||||
|
||||
struct CollectingReporter {
|
||||
errors: std::sync::Mutex<Vec<String>>,
|
||||
}
|
||||
|
||||
impl CollectingReporter {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
errors: std::sync::Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl typos_cli::report::Report for CollectingReporter {
|
||||
fn report(&self, msg: typos_cli::report::Message<'_>) -> Result<(), std::io::Error> {
|
||||
if msg.is_error() {
|
||||
self.errors.lock().unwrap().push(format!("{msg:?}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn walk_path_reports_file_removed_mid_walk() {
|
||||
let temp = assert_fs::TempDir::new().unwrap();
|
||||
let vanishing = temp.path().join("vanishing.txt");
|
||||
std::fs::write(&vanishing, b"helllo world\n").unwrap();
|
||||
|
||||
let storage = typos_cli::policy::ConfigStorage::new();
|
||||
let mut engine = typos_cli::policy::ConfigEngine::new(&storage);
|
||||
engine.set_isolated(true);
|
||||
engine.set_overrides(typos_cli::config::Config::default());
|
||||
let cwd = temp.path().canonicalize().unwrap();
|
||||
engine.init_dir(&cwd).unwrap();
|
||||
|
||||
// Remove the file as the walker visits it. `ignore` still yields it as an `Ok` entry,
|
||||
// but the `canonicalize()` in `walk_entry` then fails with `NotFound` -- the same
|
||||
// outcome as a file that a concurrent process removes mid-scan.
|
||||
let mut builder = ignore::WalkBuilder::new(temp.path());
|
||||
let target = vanishing.clone();
|
||||
let removed = std::sync::atomic::AtomicBool::new(false);
|
||||
builder.filter_entry(move |entry| {
|
||||
if entry.path() == target && !removed.swap(true, std::sync::atomic::Ordering::SeqCst) {
|
||||
std::fs::remove_file(&target).unwrap();
|
||||
}
|
||||
true
|
||||
});
|
||||
let reporter = CollectingReporter::new();
|
||||
|
||||
let payload = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
typos_cli::file::walk_path(
|
||||
builder.build(),
|
||||
&typos_cli::file::Typos,
|
||||
&engine,
|
||||
&reporter,
|
||||
false,
|
||||
)
|
||||
}))
|
||||
.expect_err("walk_path should panic on the failed policy lookup");
|
||||
let message = payload
|
||||
.downcast_ref::<String>()
|
||||
.map(String::as_str)
|
||||
.or_else(|| payload.downcast_ref::<&str>().copied());
|
||||
// `policy()` checks the path with a `debug_assert!` before reaching its `expect`.
|
||||
let expected = if cfg!(debug_assertions) {
|
||||
" is not absolute"
|
||||
} else {
|
||||
"`walk()` should be called first"
|
||||
};
|
||||
assert_eq!(message, Some(expected));
|
||||
assert!(
|
||||
!reporter.errors.lock().unwrap().is_empty(),
|
||||
"expected the vanished file's canonicalize() failure to be reported"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user