#[cfg(unix)]
use std::fs;
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
use camino::Utf8Path;
use mdtablefix::io::SourceDocument;
#[cfg(unix)]
use super::test_support::identity;
use super::{
ConflictGuard,
Mode,
analyse,
test_support::{ALIGNED, RAGGED, align, fixture, read},
};
const INTRUDER: &str = "|X|Y|\n|---|---|\n|3|4|\n";
#[test]
fn in_place_writes_the_formatted_text() {
let (_dir, directory) = fixture("ragged.md", RAGGED);
let (_report, payload) = analyse(
Mode::InPlace,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("ragged.md"),
Utf8Path::new("ragged.md"),
&align,
)
.expect("analyse fixture");
assert_eq!(payload, "");
assert_eq!(read(&directory, "ragged.md"), ALIGNED);
}
#[test]
fn in_place_formats_an_unmarked_file_without_reading_repository_state() {
let (dir, directory) = fixture("ragged.md", RAGGED);
let regular_file = dir.path().join("ragged.md");
let git_dir = Utf8Path::from_path(®ular_file).expect("the fixture path is UTF-8");
let (report, payload) = analyse(
Mode::InPlace,
&ConflictGuard::guarded(git_dir),
&directory,
Utf8Path::new("ragged.md"),
Utf8Path::new("ragged.md"),
&align,
)
.expect("an unmarked file does not query repository state");
assert!(report.is_changed);
assert_eq!(payload, "");
assert_eq!(read(&directory, "ragged.md"), ALIGNED);
}
#[cfg(unix)]
#[test]
fn in_place_replaces_a_drifting_file() {
let (dir, directory) = fixture("ragged.md", RAGGED);
let target = dir.path().join("ragged.md");
let before = fs::metadata(&target).expect("read the metadata before the write");
analyse(
Mode::InPlace,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("ragged.md"),
Utf8Path::new("ragged.md"),
&align,
)
.expect("analyse fixture");
let after = fs::metadata(&target).expect("read the metadata after the write");
assert_ne!(
before.ino(),
after.ino(),
"a drifting file must be replaced through a temporary"
);
assert_eq!(read(&directory, "ragged.md"), ALIGNED);
}
#[test]
fn in_place_declines_a_file_that_changed_under_it() {
let (dir, directory) = fixture("ragged.md", RAGGED);
let intruder_path = dir.path().join("ragged.md");
let intruder = move |document: &SourceDocument<'_>| {
std::fs::write(&intruder_path, INTRUDER).expect("write the concurrent change");
align(document)
};
let error = analyse(
Mode::InPlace,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("ragged.md"),
Utf8Path::new("ragged.md"),
&intruder,
)
.expect_err("a file that changed under the run must not be overwritten");
assert!(
error
.to_string()
.contains("changed while it was being formatted"),
"the error must say why the file was left alone: {error}"
);
assert_eq!(
read(&directory, "ragged.md"),
INTRUDER,
"the other writer's text must survive the run"
);
assert_eq!(
directory
.read_dir(".")
.expect("read the fixture directory")
.count(),
1,
"a declined write must leave no temporary file behind"
);
}
#[cfg(unix)]
#[test]
fn in_place_leaves_a_clean_file_untouched() {
let (dir, directory) = fixture("clean.md", ALIGNED);
let target = dir.path().join("clean.md");
let before = fs::metadata(&target).expect("read the metadata before the analysis");
let (report, payload) = analyse(
Mode::InPlace,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("clean.md"),
Utf8Path::new("clean.md"),
&identity,
)
.expect("analyse fixture");
let after = fs::metadata(&target).expect("read the metadata after the analysis");
assert_eq!(payload, "");
assert!(
!report.is_changed,
"the fixture is the formatter's own output"
);
assert_eq!(
before.ino(),
after.ino(),
"a clean file must not be replaced by a temporary"
);
assert_eq!(
before.mtime(),
after.mtime(),
"a clean file's modification time must not move"
);
assert_eq!(
before.mtime_nsec(),
after.mtime_nsec(),
"a clean file's modification time must not move"
);
assert_eq!(read(&directory, "clean.md"), ALIGNED);
}