239 lines
8.7 KiB
Rust
239 lines
8.7 KiB
Rust
use super::*;
|
|
|
|
#[test]
|
|
fn test_parse_range_with_comma() {
|
|
let (start, count) = DiffStateModel::parse_range("10,5")
|
|
.expect("parse_range should succeed for range with count");
|
|
assert_eq!(start, 10);
|
|
assert_eq!(count, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_range_without_comma() {
|
|
let (start, count) = DiffStateModel::parse_range("10")
|
|
.expect("parse_range should succeed for range without count");
|
|
assert_eq!(start, 10);
|
|
assert_eq!(count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_unified_diff_header_basic() {
|
|
let header = "@@ -10,5 +12,7 @@";
|
|
let parsed = DiffStateModel::parse_unified_diff_header(header)
|
|
.expect("parse_unified_diff_header should succeed for basic header");
|
|
assert_eq!(parsed.old_start_line, 10);
|
|
assert_eq!(parsed.old_line_count, 5);
|
|
assert_eq!(parsed.new_start_line, 12);
|
|
assert_eq!(parsed.new_line_count, 7);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_unified_diff_header_with_context() {
|
|
let header = "@@ -4978,33 +4978,43 @@ impl TerminalView {";
|
|
let parsed = DiffStateModel::parse_unified_diff_header(header)
|
|
.expect("parse_unified_diff_header should succeed for header with context");
|
|
assert_eq!(parsed.old_start_line, 4978);
|
|
assert_eq!(parsed.old_line_count, 33);
|
|
assert_eq!(parsed.new_start_line, 4978);
|
|
assert_eq!(parsed.new_line_count, 43);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_unified_diff_header_single_line() {
|
|
let header = "@@ -10 +12,3 @@";
|
|
let parsed = DiffStateModel::parse_unified_diff_header(header)
|
|
.expect("parse_unified_diff_header should succeed for single line header");
|
|
assert_eq!(parsed.old_start_line, 10);
|
|
assert_eq!(parsed.old_line_count, 1);
|
|
assert_eq!(parsed.new_start_line, 12);
|
|
assert_eq!(parsed.new_line_count, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sort_branches_main_first_empty() {
|
|
let branches: Vec<(String, bool)> = vec![];
|
|
let result: Vec<_> = DiffStateModel::sort_branches_main_first(&branches).collect();
|
|
assert!(result.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_sort_branches_main_first_no_main() {
|
|
let branches = vec![
|
|
("feature-a".to_string(), false),
|
|
("feature-b".to_string(), false),
|
|
("feature-c".to_string(), false),
|
|
];
|
|
let result: Vec<_> = DiffStateModel::sort_branches_main_first(&branches).collect();
|
|
// No main branches — order should be unchanged.
|
|
assert_eq!(result, branches.iter().collect::<Vec<_>>());
|
|
}
|
|
|
|
#[test]
|
|
fn test_sort_branches_main_first_promotes_main() {
|
|
let branches = vec![
|
|
("feature-a".to_string(), false),
|
|
("main".to_string(), true),
|
|
("feature-b".to_string(), false),
|
|
];
|
|
let result: Vec<_> = DiffStateModel::sort_branches_main_first(&branches)
|
|
.map(|(name, _)| name.as_str())
|
|
.collect();
|
|
assert_eq!(result, vec!["main", "feature-a", "feature-b"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sort_branches_main_first_main_already_first() {
|
|
let branches = vec![
|
|
("main".to_string(), true),
|
|
("feature-a".to_string(), false),
|
|
("feature-b".to_string(), false),
|
|
];
|
|
let result: Vec<_> = DiffStateModel::sort_branches_main_first(&branches)
|
|
.map(|(name, _)| name.as_str())
|
|
.collect();
|
|
assert_eq!(result, vec!["main", "feature-a", "feature-b"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sort_branches_main_first_preserves_recency_order_for_non_main() {
|
|
// Non-main branches should remain in their original (recency) order.
|
|
let branches = vec![
|
|
("recent-feature".to_string(), false),
|
|
("main".to_string(), true),
|
|
("older-feature".to_string(), false),
|
|
("oldest-feature".to_string(), false),
|
|
];
|
|
let result: Vec<_> = DiffStateModel::sort_branches_main_first(&branches)
|
|
.map(|(name, _)| name.as_str())
|
|
.collect();
|
|
assert_eq!(
|
|
result,
|
|
vec!["main", "recent-feature", "older-feature", "oldest-feature"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sort_branches_main_first_multiple_main_flags() {
|
|
// Defensive: both flagged as main (shouldn't happen in practice, but
|
|
// sort_branches_main_first should handle it gracefully).
|
|
let branches = vec![
|
|
("feature".to_string(), false),
|
|
("main".to_string(), true),
|
|
("master".to_string(), true),
|
|
];
|
|
let result: Vec<_> = DiffStateModel::sort_branches_main_first(&branches)
|
|
.map(|(name, _)| name.as_str())
|
|
.collect();
|
|
// Both main-flagged entries appear first, non-main last.
|
|
assert_eq!(result, vec!["main", "master", "feature"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_unified_diff_header_malformed() {
|
|
let header = "not a diff header";
|
|
let result = DiffStateModel::parse_unified_diff_header(header);
|
|
assert!(result.is_err());
|
|
|
|
let header2 = "@@ incomplete";
|
|
let result2 = DiffStateModel::parse_unified_diff_header(header2);
|
|
assert!(result2.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_modified_file_with_spaces() {
|
|
// Porcelain v2 output for a modified file with spaces in the name.
|
|
// Format: 1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>
|
|
let status_output = "1 .M N... 100644 100644 100644 abc1234 def5678 test file.txt";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(result[0].0, std::path::PathBuf::from("test file.txt"));
|
|
assert_eq!(result[0].1, GitFileStatus::Modified);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_modified_file_with_multiple_spaces() {
|
|
// Filename with multiple spaces.
|
|
let status_output = "1 .M N... 100644 100644 100644 abc1234 def5678 path to/my test file.txt";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(
|
|
result[0].0,
|
|
std::path::PathBuf::from("path to/my test file.txt")
|
|
);
|
|
assert_eq!(result[0].1, GitFileStatus::Modified);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_new_file_with_spaces() {
|
|
let status_output = "1 A. N... 000000 100644 100644 0000000 abc1234 new file name.rs";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(result[0].0, std::path::PathBuf::from("new file name.rs"));
|
|
assert_eq!(result[0].1, GitFileStatus::New);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_renamed_file_with_spaces() {
|
|
// Porcelain v2 renamed entry (type 2) with spaces in the new path.
|
|
// Format: 2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path>\0<origPath>
|
|
let status_output =
|
|
"2 R. N... 100644 100644 100644 abc1234 def5678 R100 new name.txt\0old name.txt";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(result[0].0, std::path::PathBuf::from("new name.txt"));
|
|
assert!(matches!(
|
|
&result[0].1,
|
|
GitFileStatus::Renamed { old_path } if old_path == "old name.txt"
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_untracked_file_with_spaces() {
|
|
let status_output = "? my untracked file.txt";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(
|
|
result[0].0,
|
|
std::path::PathBuf::from("my untracked file.txt")
|
|
);
|
|
assert_eq!(result[0].1, GitFileStatus::Untracked);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_unmerged_file_with_spaces() {
|
|
// Porcelain v2 unmerged entry (type u) with spaces in the path.
|
|
// Format: u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
|
|
let status_output =
|
|
"u UU N... 100644 100644 100644 100644 abc1234 def5678 ghi9012 conflict file.txt";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(result[0].0, std::path::PathBuf::from("conflict file.txt"));
|
|
assert_eq!(result[0].1, GitFileStatus::Conflicted);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_mixed_entries_with_spaces() {
|
|
// Multiple entries separated by NUL, mixing files with and without spaces.
|
|
let status_output = "1 .M N... 100644 100644 100644 abc1234 def5678 test file.txt\0\
|
|
1 .M N... 100644 100644 100644 abc1234 def5678 normal.txt\0\
|
|
? another file with spaces.rs";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 3);
|
|
assert_eq!(result[0].0, std::path::PathBuf::from("test file.txt"));
|
|
assert_eq!(result[1].0, std::path::PathBuf::from("normal.txt"));
|
|
assert_eq!(
|
|
result[2].0,
|
|
std::path::PathBuf::from("another file with spaces.rs")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_git_status_file_without_spaces_still_works() {
|
|
// Ensure the splitn change doesn't break files without spaces.
|
|
let status_output = "1 .M N... 100644 100644 100644 abc1234 def5678 simple.txt";
|
|
let result = DiffStateModel::parse_git_status(status_output).unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(result[0].0, std::path::PathBuf::from("simple.txt"));
|
|
assert_eq!(result[0].1, GitFileStatus::Modified);
|
|
}
|