refactor: move embedded tests into dedicated test directory
This commit is contained in:
+2
-427
@@ -456,430 +456,5 @@ fn changes(before: &str, after: &str) -> Vec<Option<Change>> {
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ffi::OsString;
|
||||
|
||||
static NEXT_TEST: AtomicU64 = AtomicU64::new(0);
|
||||
struct Temporary(PathBuf);
|
||||
impl Temporary {
|
||||
fn new() -> Self {
|
||||
let path = std::env::temp_dir().join(format!(
|
||||
"vamp-gutter-test-{}-{}",
|
||||
std::process::id(),
|
||||
NEXT_TEST.fetch_add(1, Ordering::Relaxed)
|
||||
));
|
||||
fs::create_dir(&path).unwrap();
|
||||
Self(path)
|
||||
}
|
||||
}
|
||||
impl Drop for Temporary {
|
||||
fn drop(&mut self) {
|
||||
let _ = fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
fn command(root: &Path, program: &str, args: &[&str]) -> String {
|
||||
let mut command = Command::new(program);
|
||||
if program == "jj" {
|
||||
command.args([
|
||||
"--config=user.name=\"Gutter Author\"",
|
||||
"--config=user.email=\"gutter@example.test\"",
|
||||
"--config=debug.commit-timestamp=\"2001-01-01T00:00:00Z\"",
|
||||
"--config=debug.operation-timestamp=\"2001-01-01T00:00:00Z\"",
|
||||
]);
|
||||
}
|
||||
let output = command
|
||||
.args(args)
|
||||
.current_dir(root)
|
||||
.env("GIT_CONFIG_GLOBAL", "/dev/null")
|
||||
.env("GIT_CONFIG_SYSTEM", "/dev/null")
|
||||
.env("GIT_AUTHOR_DATE", "2001-01-01T00:00:00Z")
|
||||
.env("GIT_COMMITTER_DATE", "2001-01-01T00:00:00Z")
|
||||
.env("JJ_CONFIG", "/dev/null")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
String::from_utf8(output.stdout).unwrap().trim().into()
|
||||
}
|
||||
fn git_fixture() -> (Temporary, PathBuf) {
|
||||
let temp = Temporary::new();
|
||||
command(&temp.0, "git", &["init", "--quiet", "--template="]);
|
||||
command(&temp.0, "git", &["config", "user.name", "Gutter Author"]);
|
||||
command(
|
||||
&temp.0,
|
||||
"git",
|
||||
&["config", "user.email", "gutter@example.test"],
|
||||
);
|
||||
let path = temp.0.join("-literal [file]\n\t\".txt");
|
||||
fs::write(&path, "first\nsecond\nthird\n").unwrap();
|
||||
command(
|
||||
&temp.0,
|
||||
"git",
|
||||
&["add", "--", path.file_name().unwrap().to_str().unwrap()],
|
||||
);
|
||||
command(&temp.0, "git", &["commit", "--quiet", "-m", "Baseline"]);
|
||||
(temp, path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maps_replacements_surplus_deletions_empty_and_eof_rows() {
|
||||
use Change::*;
|
||||
for (before, after, expected) in [
|
||||
("first\nsecond\n", "first\nsecond\n", vec![None, None, None]),
|
||||
(
|
||||
"first\r\nsecond\r\n",
|
||||
"first\nsecond\n",
|
||||
vec![None, None, None],
|
||||
),
|
||||
("first\rsecond\r", "first\nsecond\n", vec![None, None, None]),
|
||||
(
|
||||
"first\r\nsecond\r\nthird\r\n",
|
||||
"first\nchanged\nthird\n",
|
||||
vec![None, Some(Modified), None, None],
|
||||
),
|
||||
(
|
||||
"first\nsecond\nthird\n",
|
||||
"first\rchanged\rthird\r",
|
||||
vec![None, Some(Modified), None, None],
|
||||
),
|
||||
("first\n", "first\nnew\n", vec![None, Some(Added), None]),
|
||||
(
|
||||
"old\n",
|
||||
"changed\nsurplus\n",
|
||||
vec![Some(Modified), Some(Added), None],
|
||||
),
|
||||
(
|
||||
"old\nextra\nlast\n",
|
||||
"changed\nlast\n",
|
||||
vec![Some(Modified), None, None],
|
||||
),
|
||||
(
|
||||
"first\nremoved\nlast\n",
|
||||
"first\nlast\n",
|
||||
vec![None, Some(Removed), None],
|
||||
),
|
||||
("first\nremoved\n", "first\n", vec![None, Some(Removed)]),
|
||||
("first\nremoved", "first", vec![Some(Modified)]),
|
||||
("first\nremoved", "first\n", vec![None, Some(Removed)]),
|
||||
("removed\n", "", vec![Some(Removed)]),
|
||||
("", "new\n", vec![Some(Added), None]),
|
||||
("", "", vec![None]),
|
||||
("same", "same\n", vec![Some(Modified), None]),
|
||||
] {
|
||||
assert_eq!(changes(before, after), expected, "{before:?} -> {after:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_crlf_baseline_matches_normalized_editor_rows() {
|
||||
let (temp, _) = git_fixture();
|
||||
let path = temp.0.join("crlf");
|
||||
fs::write(&path, "first\r\nsecond\r\nthird\r\n").unwrap();
|
||||
command(&temp.0, "git", &["add", "--", "crlf"]);
|
||||
command(
|
||||
&temp.0,
|
||||
"git",
|
||||
&["commit", "--quiet", "-m", "CRLF baseline"],
|
||||
);
|
||||
assert_eq!(
|
||||
read_file(&path, "first\nsecond\nthird\n", &|| false).unwrap(),
|
||||
vec![None, None, None, None]
|
||||
);
|
||||
assert_eq!(
|
||||
read_file(&path, "first\nchanged\nthird\n", &|| false).unwrap(),
|
||||
vec![None, Some(Change::Modified), None, None]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_head_includes_staged_disk_and_unsaved_edits_without_writes() {
|
||||
let (temp, path) = git_fixture();
|
||||
fs::write(&path, "staged\nsecond\nthird\n").unwrap();
|
||||
command(
|
||||
&temp.0,
|
||||
"git",
|
||||
&["add", "--", path.file_name().unwrap().to_str().unwrap()],
|
||||
);
|
||||
fs::write(&path, "staged\ndisk\nthird\n").unwrap();
|
||||
let index = fs::read(temp.0.join(".git/index")).unwrap();
|
||||
let head = command(&temp.0, "git", &["rev-parse", "HEAD"]);
|
||||
assert_eq!(
|
||||
read_file(&path, "staged\ndisk\nunsaved\n", &|| false).unwrap(),
|
||||
vec![
|
||||
Some(Change::Modified),
|
||||
Some(Change::Modified),
|
||||
Some(Change::Modified),
|
||||
None
|
||||
]
|
||||
);
|
||||
assert_eq!(fs::read(temp.0.join(".git/index")).unwrap(), index);
|
||||
assert_eq!(command(&temp.0, "git", &["rev-parse", "HEAD"]), head);
|
||||
assert_eq!(fs::read_to_string(&path).unwrap(), "staged\ndisk\nthird\n");
|
||||
fs::remove_file(&path).unwrap();
|
||||
assert_eq!(
|
||||
read_file(&path, "first\nsecond\nthird\n", &|| false).unwrap(),
|
||||
vec![None, None, None, None]
|
||||
);
|
||||
assert_eq!(
|
||||
read_file(&temp.0.join("new/named"), "unsaved\n", &|| false).unwrap(),
|
||||
vec![Some(Change::Added), None]
|
||||
);
|
||||
let untracked = temp.0.join("untracked");
|
||||
fs::write(&untracked, "unsaved\n").unwrap();
|
||||
assert_eq!(
|
||||
read_file(&untracked, "unsaved\n", &|| false).unwrap(),
|
||||
vec![Some(Change::Added), None]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unborn_untracked_missing_ignored_metadata_binary_and_symlink_paths() {
|
||||
let temp = Temporary::new();
|
||||
let path = temp.0.join("new/named");
|
||||
assert!(read_file(&path, "unsaved\n", &|| false).unwrap().is_empty());
|
||||
command(&temp.0, "git", &["init", "--quiet", "--template="]);
|
||||
assert_eq!(
|
||||
read_file(&path, "unsaved\n", &|| false).unwrap(),
|
||||
vec![Some(Change::Added), None]
|
||||
);
|
||||
let disk = temp.0.join("untracked");
|
||||
fs::write(&disk, "disk\n").unwrap();
|
||||
assert_eq!(
|
||||
read_file(&disk, "unsaved\n", &|| false).unwrap(),
|
||||
vec![Some(Change::Added), None]
|
||||
);
|
||||
fs::write(temp.0.join(".gitignore"), "new/\nuntracked\n").unwrap();
|
||||
assert!(read_file(&path, "unsaved\n", &|| false).unwrap().is_empty());
|
||||
assert!(read_file(&disk, "unsaved\n", &|| false).unwrap().is_empty());
|
||||
assert!(
|
||||
read_file(&temp.0.join(".git/config"), "text\n", &|| false)
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
assert!(
|
||||
read_file(&disk, "binary\0text", &|| false)
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
std::os::unix::fs::symlink(&disk, temp.0.join("link")).unwrap();
|
||||
assert!(
|
||||
read_file(&temp.0.join("link"), "text\n", &|| false)
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
std::os::unix::fs::symlink(&temp.0, temp.0.join("linked")).unwrap();
|
||||
assert!(
|
||||
read_file(&temp.0.join("linked/untracked"), "text\n", &|| false)
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_non_utf8_filename_is_literal() {
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
let (temp, _) = git_fixture();
|
||||
let name = OsString::from_vec(b"non-utf8-\xff".to_vec());
|
||||
let path = temp.0.join(&name);
|
||||
match fs::write(&path, "old\n") {
|
||||
Ok(()) => {}
|
||||
// APFS rejects non-UTF-8 names before repository code can read them.
|
||||
Err(error) if cfg!(target_os = "macos") && error.raw_os_error() == Some(92) => return,
|
||||
Err(error) => panic!("Cannot create non-UTF-8 fixture: {error}"),
|
||||
}
|
||||
let mut add = git(&temp.0);
|
||||
add.args(["add", "--"]).arg(&name);
|
||||
checked(add, &|| false).unwrap();
|
||||
command(&temp.0, "git", &["commit", "--quiet", "-m", "Byte path"]);
|
||||
assert_eq!(
|
||||
read_file(&path, "changed\n", &|| false).unwrap(),
|
||||
vec![Some(Change::Modified), None]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jj_parent_root_colocation_ignore_and_merge_queries_are_read_only() {
|
||||
for colocate in ["--colocate", "--no-colocate"] {
|
||||
let temp = Temporary::new();
|
||||
command(&temp.0, "jj", &["git", "init", colocate, "."]);
|
||||
let name = "-literal [file]\n\t\".txt";
|
||||
let path = temp.0.join(name);
|
||||
fs::write(&path, "original\n").unwrap();
|
||||
assert_eq!(
|
||||
read_file(&path, "unsaved\n", &|| false).unwrap(),
|
||||
vec![Some(Change::Added), None]
|
||||
);
|
||||
fs::write(temp.0.join(".gitignore"), "ignored\n").unwrap();
|
||||
assert!(
|
||||
read_file(&temp.0.join("ignored"), "text\n", &|| false)
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
command(&temp.0, "jj", &["describe", "-m", "Baseline"]);
|
||||
command(&temp.0, "jj", &["new"]);
|
||||
let operation = command(
|
||||
&temp.0,
|
||||
"jj",
|
||||
&[
|
||||
"--ignore-working-copy",
|
||||
"op",
|
||||
"log",
|
||||
"--no-graph",
|
||||
"-n",
|
||||
"1",
|
||||
"-T",
|
||||
"id",
|
||||
],
|
||||
);
|
||||
let store = store_for_jj(&temp.0).unwrap();
|
||||
let refs = fs::read(store.join("HEAD")).unwrap();
|
||||
fs::write(&path, "disk edit\n").unwrap();
|
||||
assert_eq!(
|
||||
read_file(&path, "inserted\noriginal\n", &|| false).unwrap(),
|
||||
vec![Some(Change::Added), None, None]
|
||||
);
|
||||
assert_eq!(fs::read(store.join("HEAD")).unwrap(), refs);
|
||||
assert_eq!(
|
||||
command(
|
||||
&temp.0,
|
||||
"jj",
|
||||
&[
|
||||
"--ignore-working-copy",
|
||||
"op",
|
||||
"log",
|
||||
"--no-graph",
|
||||
"-n",
|
||||
"1",
|
||||
"-T",
|
||||
"id",
|
||||
]
|
||||
),
|
||||
operation
|
||||
);
|
||||
let parent = command(
|
||||
&temp.0,
|
||||
"jj",
|
||||
&[
|
||||
"--ignore-working-copy",
|
||||
"log",
|
||||
"--no-graph",
|
||||
"-r",
|
||||
"@-",
|
||||
"-T",
|
||||
"commit_id",
|
||||
],
|
||||
);
|
||||
command(&temp.0, "jj", &["new", "root()"]);
|
||||
fs::write(temp.0.join("sibling"), "sibling\n").unwrap();
|
||||
command(&temp.0, "jj", &["describe", "-m", "Sibling"]);
|
||||
command(&temp.0, "jj", &["new", &parent, "@"]);
|
||||
assert!(
|
||||
read_file(&path, "original\n", &|| false)
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn worker_retains_only_latest_batch_and_file_errors_keep_good_siblings() {
|
||||
let (_temp, path) = git_fixture();
|
||||
let mut worker = Worker::start().unwrap();
|
||||
let oversized: Arc<str> = "x".repeat(FILE_LIMIT + 1).into();
|
||||
for id in 0..20 {
|
||||
worker
|
||||
.request(
|
||||
id,
|
||||
vec![
|
||||
(5, path.clone(), "first\nchanged\nthird\n".into()),
|
||||
(8, path.clone(), Arc::clone(&oversized)),
|
||||
],
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
loop {
|
||||
if let Some((id, result, picker)) = worker.poll() {
|
||||
assert!(picker.is_none());
|
||||
assert_eq!(id, 19);
|
||||
assert_eq!(result[0].0, 5);
|
||||
assert_eq!(
|
||||
result[0].1.as_ref().unwrap(),
|
||||
&vec![None, Some(Change::Modified), None, None]
|
||||
);
|
||||
assert_eq!(result[1].0, 8);
|
||||
assert_eq!(
|
||||
result[1].1.as_ref().unwrap_err(),
|
||||
"Gutter file exceeds 16 MiB"
|
||||
);
|
||||
break;
|
||||
}
|
||||
thread::yield_now();
|
||||
}
|
||||
assert_eq!(
|
||||
worker
|
||||
.request(
|
||||
20,
|
||||
vec![(0, path, "x".repeat(BATCH_LIMIT + 1).into())],
|
||||
None
|
||||
)
|
||||
.unwrap_err()
|
||||
.kind(),
|
||||
io::ErrorKind::InvalidInput
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn picker_status_is_optional_and_errors_do_not_discard_line_results() {
|
||||
let (temp, path) = git_fixture();
|
||||
let mut worker = Worker::start().unwrap();
|
||||
worker
|
||||
.request(
|
||||
1,
|
||||
vec![],
|
||||
Some((
|
||||
temp.0.clone(),
|
||||
vec![(path.clone(), "dirty buffer\n".into())],
|
||||
)),
|
||||
)
|
||||
.unwrap();
|
||||
loop {
|
||||
if let Some((id, lines, picker)) = worker.poll() {
|
||||
assert_eq!(id, 1);
|
||||
assert!(lines.is_empty());
|
||||
let picker = picker.unwrap();
|
||||
assert_eq!(picker.root, temp.0);
|
||||
let changes = picker.result.unwrap();
|
||||
assert_eq!(changes.len(), 1);
|
||||
assert_eq!(changes[0].path, path.strip_prefix(&temp.0).unwrap());
|
||||
assert_eq!(changes[0].status, crate::diff::FileStatus::Modified);
|
||||
break;
|
||||
}
|
||||
thread::yield_now();
|
||||
}
|
||||
worker
|
||||
.request(
|
||||
2,
|
||||
vec![(4, path.clone(), "first\nchanged\nthird\n".into())],
|
||||
Some((
|
||||
temp.0.clone(),
|
||||
vec![(path, "x".repeat(FILE_LIMIT + 1).into())],
|
||||
)),
|
||||
)
|
||||
.unwrap();
|
||||
loop {
|
||||
if let Some((id, lines, picker)) = worker.poll() {
|
||||
assert_eq!(id, 2);
|
||||
assert_eq!(lines[0].0, 4);
|
||||
assert!(lines[0].1.is_ok());
|
||||
assert!(picker.unwrap().result.unwrap_err().contains("16 MiB"));
|
||||
break;
|
||||
}
|
||||
thread::yield_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
#[path = "../tests/unit/gutter.rs"]
|
||||
mod tests;
|
||||
|
||||
Reference in New Issue
Block a user