Skip to content

Commit ef91cfb

Browse files
committed
Add unit tests from cargo-mutants in indexing/store
platform_case_compare backs the SQLite `platform_case` collation that every path-resolution query relies on. The only existing test was the macOS happy-path checking that "Users", "users", and "USERS" all resolve to the same row — but that doesn't catch the mutant where platform_case_compare always returns Ordering::Equal (the Default::default() replacement), because every name-vs-name comparison would still be Equal, including across distinct entries. Six new tests directly exercise the private collation: macOS (case-insensitive, NFD-normalizing): - platform_case_compare_distinguishes_distinct_names: pin Less/Greater for a-vs-b and b-vs-a — kills the Default::default() mutant. - platform_case_compare_case_insensitive_on_macos: "Users" == "users" and "README.MD" == "readme.md". - platform_case_compare_normalizes_unicode_nfc_to_nfd: NFC "café" and NFD "cafe + combining acute" compare equal — pins the APFS-matching normalization step. - normalize_for_comparison_lowercases_and_nfd_normalizes: NFC and NFD produce the same normalized form, output is non-empty for non-empty input — kills the empty/sentinel replacement mutants. Linux/other (binary): - platform_case_compare_is_binary_off_macos: 'U' < 'u' so "Users" > "users" — pins the cfg-gated identity path. - normalize_for_comparison_is_identity_off_macos: returns the input string unchanged. Plus a trailing-blank-line cleanup in watcher_test.rs (rustfmt).
1 parent a812cd9 commit ef91cfb

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

apps/desktop/src-tauri/src/file_system/watcher_test.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ fn diff_includes_add_modify_and_remove_in_one_pass() {
190190
assert_eq!(remove.index, 1, "remove uses OLD index");
191191
}
192192

193-
194193
// ============================================================================
195194
// handle_directory_change integration tests
196195
// ============================================================================

apps/desktop/src-tauri/src/indexing/store.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,88 @@ mod tests {
21632163
assert!(IndexStore::has_sized_entry_for_inode(&conn, 100, None).unwrap());
21642164
}
21652165

2166+
// ====================================================================
2167+
// platform_case_compare / normalize_for_comparison
2168+
//
2169+
// The collation function backs SQLite's `platform_case` collation, which
2170+
// every path-resolution query relies on. cargo-mutants showed the
2171+
// structural mutants `platform_case_compare -> Default::default()` and
2172+
// `normalize_for_comparison -> String::new() / "xyzzy".into()` survive
2173+
// when the only test exercises one direction of equality.
2174+
// ====================================================================
2175+
2176+
#[cfg(target_os = "macos")]
2177+
#[test]
2178+
fn platform_case_compare_distinguishes_distinct_names() {
2179+
// Kills: replace platform_case_compare -> Default::default() (which is
2180+
// Ordering::Equal, so every comparison would say "equal" — sort order
2181+
// and SQLite's collation-driven uniqueness would collapse).
2182+
assert_eq!(platform_case_compare("a", "a"), std::cmp::Ordering::Equal);
2183+
assert_eq!(platform_case_compare("a", "b"), std::cmp::Ordering::Less);
2184+
assert_eq!(platform_case_compare("b", "a"), std::cmp::Ordering::Greater);
2185+
}
2186+
2187+
#[cfg(target_os = "macos")]
2188+
#[test]
2189+
fn platform_case_compare_case_insensitive_on_macos() {
2190+
// APFS is case-preserving but case-insensitive by default. The
2191+
// collation must report equality across case variants for path
2192+
// resolution to work.
2193+
assert_eq!(platform_case_compare("Users", "users"), std::cmp::Ordering::Equal);
2194+
assert_eq!(
2195+
platform_case_compare("README.MD", "readme.md"),
2196+
std::cmp::Ordering::Equal
2197+
);
2198+
}
2199+
2200+
#[cfg(target_os = "macos")]
2201+
#[test]
2202+
fn platform_case_compare_normalizes_unicode_nfc_to_nfd() {
2203+
// "é" can be one codepoint (NFC, U+00E9) or two (NFD, U+0065 U+0301).
2204+
// APFS stores NFD; the collation must treat the two representations
2205+
// as equal so a user typing NFC resolves NFD-stored entries.
2206+
let nfc = "café"; // typically NFC in Rust source
2207+
let nfd = "cafe\u{0301}"; // 'e' + combining acute
2208+
// Make sure they're actually different byte sequences (sanity check).
2209+
assert_ne!(nfc.as_bytes(), nfd.as_bytes());
2210+
assert_eq!(
2211+
platform_case_compare(nfc, nfd),
2212+
std::cmp::Ordering::Equal,
2213+
"NFC and NFD forms of 'café' must compare equal on APFS"
2214+
);
2215+
}
2216+
2217+
#[cfg(not(target_os = "macos"))]
2218+
#[test]
2219+
fn platform_case_compare_is_binary_off_macos() {
2220+
// Linux ext4/btrfs: exact byte comparison, NOT case-folded.
2221+
assert_eq!(platform_case_compare("a", "a"), std::cmp::Ordering::Equal);
2222+
assert_eq!(platform_case_compare("Users", "users"), std::cmp::Ordering::Greater);
2223+
// ('U' = 0x55, 'u' = 0x75 → 'U' < 'u' in ASCII, so "Users" < "users".)
2224+
}
2225+
2226+
#[cfg(target_os = "macos")]
2227+
#[test]
2228+
fn normalize_for_comparison_lowercases_and_nfd_normalizes() {
2229+
// Kills: replace normalize_for_comparison -> String::new() / "xyzzy".
2230+
assert_eq!(normalize_for_comparison("Users"), "users");
2231+
let nfc = "café";
2232+
let nfd = "cafe\u{0301}";
2233+
// After normalization, both should be NFD-lowercased and equal.
2234+
assert_eq!(normalize_for_comparison(nfc), normalize_for_comparison(nfd));
2235+
assert!(
2236+
!normalize_for_comparison("hello").is_empty(),
2237+
"normalize_for_comparison must not return an empty string for non-empty input"
2238+
);
2239+
}
2240+
2241+
#[cfg(not(target_os = "macos"))]
2242+
#[test]
2243+
fn normalize_for_comparison_is_identity_off_macos() {
2244+
assert_eq!(normalize_for_comparison("Users"), "Users");
2245+
assert_eq!(normalize_for_comparison("hello"), "hello");
2246+
}
2247+
21662248
#[test]
21672249
fn has_sized_entry_for_inode_multiple_entries_one_has_sizes() {
21682250
let (_store, dir) = open_temp_store();

0 commit comments

Comments
 (0)