mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Add automatic dedup suffix stripping to enhance syncback functionality
- Introduced a new plan to automatically strip `~N` dedup suffixes from filenames during forward sync, eliminating the need for redundant `.meta.json5` files. - Implemented the `strip_dedup_suffix()` utility in `dedup_suffix.rs` to handle suffix removal. - Updated `file_name_for_path()` and `get_dir_middleware()` to utilize the new stripping functionality. - Modified syncback logic to skip writing meta `name` fields for dedup-only cases, ensuring cleaner instance naming. - Enhanced existing tests and added new tests to cover the updated dedup behavior, ensuring robustness and correctness. These changes aim to streamline the handling of deduplicated instances, improving overall system efficiency and clarity in naming conventions.
This commit is contained in:
parent
17d52e3edf
commit
7d0232c430
19 changed files with 269 additions and 84 deletions
|
|
@ -1556,8 +1556,9 @@ impl JobThreadContext {
|
|||
// Always update the name field when the
|
||||
// instance name changed, even if the path didn't
|
||||
// change (e.g. "Foo/Bar" → "Foo|Bar" both slugify
|
||||
// to "Foo_Bar"). Use deduped_new_name because
|
||||
// dedup may have appended ~N.
|
||||
// to "Foo_Bar"). Only slugification requires a
|
||||
// meta name -- dedup suffixes (~N) are stripped
|
||||
// automatically by forward sync.
|
||||
//
|
||||
// For .model.json5/.model.json files, the name
|
||||
// field lives INSIDE the model file, not in
|
||||
|
|
@ -1566,7 +1567,7 @@ impl JobThreadContext {
|
|||
let model_file = overridden_source_path
|
||||
.as_deref()
|
||||
.unwrap_or(path.as_path());
|
||||
if deduped_new_name != *new_name {
|
||||
if slugified_new_name != *new_name {
|
||||
self.upsert_model_name_field(
|
||||
model_file, new_name,
|
||||
);
|
||||
|
|
@ -1578,7 +1579,7 @@ impl JobThreadContext {
|
|||
"{}.meta.json5",
|
||||
effective_meta_base
|
||||
));
|
||||
if deduped_new_name != *new_name {
|
||||
if slugified_new_name != *new_name {
|
||||
self.upsert_meta_name_field(
|
||||
¤t_meta,
|
||||
new_name,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ use anyhow::Context;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
glob::Glob, path_serializer, project::ProjectNode, snapshot_middleware::Middleware, RojoRef,
|
||||
glob::Glob, path_serializer, project::ProjectNode, snapshot_middleware::Middleware,
|
||||
syncback::dedup_suffix::strip_dedup_suffix, RojoRef,
|
||||
};
|
||||
|
||||
/// Rojo-specific metadata that can be associated with an instance or a snapshot
|
||||
|
|
@ -328,23 +329,22 @@ impl SyncRule {
|
|||
}
|
||||
|
||||
pub fn file_name_for_path<'a>(&self, path: &'a Path) -> anyhow::Result<&'a str> {
|
||||
if let Some(suffix) = &self.suffix {
|
||||
let name = if let Some(suffix) = &self.suffix {
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.with_context(|| format!("file name of {} is invalid", path.display()))?;
|
||||
if file_name.ends_with(suffix) {
|
||||
let end = file_name.len().saturating_sub(suffix.len());
|
||||
Ok(&file_name[..end])
|
||||
&file_name[..end]
|
||||
} else {
|
||||
Ok(file_name)
|
||||
file_name
|
||||
}
|
||||
} else {
|
||||
// If the user doesn't specify a suffix, we assume they just want
|
||||
// the name of the file (the file_stem)
|
||||
path.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.with_context(|| format!("file name of {} is invalid", path.display()))
|
||||
}
|
||||
.with_context(|| format!("file name of {} is invalid", path.display()))?
|
||||
};
|
||||
Ok(strip_dedup_suffix(name))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::{
|
||||
glob::Glob,
|
||||
project::DEFAULT_PROJECT_NAMES,
|
||||
syncback::{SyncbackReturn, SyncbackSnapshot},
|
||||
syncback::{dedup_suffix::strip_dedup_suffix, SyncbackReturn, SyncbackSnapshot},
|
||||
};
|
||||
use crate::{
|
||||
snapshot::{InstanceContext, InstanceSnapshot, SyncRule},
|
||||
|
|
@ -118,6 +118,7 @@ fn get_dir_middleware<'path>(
|
|||
.expect("Could not extract directory name")
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("File name was not valid UTF-8: {}", dir_path.display()))?;
|
||||
let dir_name = strip_dedup_suffix(dir_name);
|
||||
|
||||
static INIT_PATHS: OnceLock<Vec<(Middleware, &str)>> = OnceLock::new();
|
||||
let order = INIT_PATHS.get_or_init(|| {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,16 @@ pub fn parse_dedup_suffix(stem: &str) -> Option<(&str, u32)> {
|
|||
None
|
||||
}
|
||||
|
||||
/// Strips a `~N` dedup suffix from a filename stem, returning the base name.
|
||||
///
|
||||
/// Given `"Foo~2"`, returns `"Foo"`.
|
||||
/// Given `"Foo"`, returns `"Foo"` unchanged.
|
||||
/// Given `"Foo~0"` or `"Foo~abc"`, returns the input unchanged (not valid
|
||||
/// dedup suffixes).
|
||||
pub fn strip_dedup_suffix(name: &str) -> &str {
|
||||
parse_dedup_suffix(name).map_or(name, |(base, _)| base)
|
||||
}
|
||||
|
||||
/// Builds a dedup'd filename from a base stem, optional suffix number, and
|
||||
/// extension.
|
||||
///
|
||||
|
|
@ -175,6 +185,22 @@ mod tests {
|
|||
assert_eq!(parse_dedup_suffix("My Script~1"), Some(("My Script", 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_suffix_basic() {
|
||||
assert_eq!(strip_dedup_suffix("Foo~1"), "Foo");
|
||||
assert_eq!(strip_dedup_suffix("Foo~2"), "Foo");
|
||||
assert_eq!(strip_dedup_suffix("Foo~10"), "Foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_suffix_no_op() {
|
||||
assert_eq!(strip_dedup_suffix("Foo"), "Foo");
|
||||
assert_eq!(strip_dedup_suffix("Foo~0"), "Foo~0");
|
||||
assert_eq!(strip_dedup_suffix("Foo~abc"), "Foo~abc");
|
||||
assert_eq!(strip_dedup_suffix("Foo~"), "Foo~");
|
||||
assert_eq!(strip_dedup_suffix(""), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_name_file() {
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -75,8 +75,9 @@ pub fn name_for_inst<'a>(
|
|||
Some(extension_for_middleware(middleware))
|
||||
};
|
||||
|
||||
let (deduped_slug, full_fs_name) = deduplicate_name_with_ext(&base, extension, taken_names);
|
||||
let needs_meta = needs_slugify || deduped_slug != base;
|
||||
let (_deduped_slug, full_fs_name) =
|
||||
deduplicate_name_with_ext(&base, extension, taken_names);
|
||||
let needs_meta = needs_slugify;
|
||||
|
||||
let filename = full_fs_name.clone();
|
||||
Ok((Cow::Owned(filename), needs_meta, full_fs_name))
|
||||
|
|
@ -832,7 +833,10 @@ mod tests {
|
|||
let (filename, needs_meta, _dk) =
|
||||
name_for_inst(Middleware::ModuleScript, child, None, &taken).unwrap();
|
||||
assert_eq!(filename.as_ref(), "Foo~2.luau");
|
||||
assert!(needs_meta, "deduped name differs from original");
|
||||
assert!(
|
||||
!needs_meta,
|
||||
"dedup-only: forward sync strips ~N automatically"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -845,7 +849,10 @@ mod tests {
|
|||
let (filename, needs_meta, _dk) =
|
||||
name_for_inst(Middleware::Dir, child, None, &taken).unwrap();
|
||||
assert_eq!(filename.as_ref(), "Stuff~2");
|
||||
assert!(needs_meta);
|
||||
assert!(
|
||||
!needs_meta,
|
||||
"dedup-only: forward sync strips ~N automatically"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1172,21 +1179,19 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn nfi_clean_name_dedup_still_needs_meta_dir() {
|
||||
// Clean name "Foo", but "foo" is already taken → dedup adds ~2 → needs meta
|
||||
fn nfi_clean_name_dedup_no_meta_dir() {
|
||||
// Clean name "Foo", but "foo" is already taken → dedup adds ~2.
|
||||
// Forward sync strips ~N, so no meta name needed.
|
||||
let (f, m) = nfi("Foo", Middleware::Dir, &["foo"]);
|
||||
assert_eq!(f, "Foo~2");
|
||||
assert!(
|
||||
m,
|
||||
"dedup suffix means filesystem name differs from instance name"
|
||||
);
|
||||
assert!(!m, "dedup-only: forward sync strips ~N automatically");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nfi_case_collision_dir() {
|
||||
let (f, m) = nfi("Assets", Middleware::Dir, &["assets"]);
|
||||
assert_eq!(f, "Assets~2");
|
||||
assert!(m);
|
||||
assert!(!m, "dedup-only: forward sync strips ~N automatically");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1223,7 +1228,7 @@ mod tests {
|
|||
assert_eq!(r[0].1, "Foo");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "Foo~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: forward sync strips ~N automatically");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1251,7 +1256,7 @@ mod tests {
|
|||
assert_eq!(r[0].1, "Hey_Bro");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "Hey_Bro~2");
|
||||
assert!(r[1].2, "dedup suffix → needs meta even for natural name");
|
||||
assert!(!r[1].2, "dedup-only: natural name, forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1260,9 +1265,9 @@ mod tests {
|
|||
assert_eq!(r[0].1, "Script");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "script~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: forward sync strips ~N automatically");
|
||||
assert_eq!(r[2].1, "SCRIPT~3");
|
||||
assert!(r[2].2);
|
||||
assert!(!r[2].2, "dedup-only: forward sync strips ~N automatically");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1294,7 +1299,7 @@ mod tests {
|
|||
assert_eq!(r[0].1, "CON_");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "CON_~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: natural name, forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1309,11 +1314,11 @@ mod tests {
|
|||
assert_eq!(r[0].1, "Utils");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "utils~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[2].1, "Utils~3");
|
||||
assert!(r[2].2);
|
||||
assert!(!r[2].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[3].1, "UTILS~4");
|
||||
assert!(r[3].2);
|
||||
assert!(!r[3].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[4].1, "Uti_ls");
|
||||
assert!(r[4].2);
|
||||
}
|
||||
|
|
@ -1351,9 +1356,9 @@ mod tests {
|
|||
assert_eq!(r[0].1, "Leading");
|
||||
assert!(r[0].2, "leading space was stripped");
|
||||
assert_eq!(r[1].1, "Leading~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: clean name, forward sync strips ~N");
|
||||
assert_eq!(r[2].1, "Leading~3");
|
||||
assert!(r[2].2);
|
||||
assert!(r[2].2, "trailing space stripped → slug differs");
|
||||
}
|
||||
|
||||
// ── Every middleware type with slugified name ─────────────────────
|
||||
|
|
@ -1559,7 +1564,7 @@ mod tests {
|
|||
// must get "Utils~2".
|
||||
let r = process_new_children_with_disk_seed(&["utils"], &[("Utils", Middleware::Dir)]);
|
||||
assert_eq!(r[0].1, "Utils~2");
|
||||
assert!(r[0].2);
|
||||
assert!(!r[0].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1567,7 +1572,7 @@ mod tests {
|
|||
// "SCRIPTS" directory on disk, new instance "Scripts" (dir) must dedup.
|
||||
let r = process_new_children_with_disk_seed(&["scripts"], &[("Scripts", Middleware::Dir)]);
|
||||
assert_eq!(r[0].1, "Scripts~2");
|
||||
assert!(r[0].2);
|
||||
assert!(!r[0].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1592,9 +1597,9 @@ mod tests {
|
|||
],
|
||||
);
|
||||
assert_eq!(r[0].1, "Utils~2", "Utils collides with utils/ on disk");
|
||||
assert!(r[0].2);
|
||||
assert!(!r[0].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[1].1, "Config~2", "Config collides with config/ on disk");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[2].1, "NewThing", "NewThing is clean");
|
||||
assert!(!r[2].2);
|
||||
}
|
||||
|
|
@ -1608,7 +1613,7 @@ mod tests {
|
|||
&[("Stuff", Middleware::Dir)],
|
||||
);
|
||||
assert_eq!(r[0].1, "Stuff~2");
|
||||
assert!(r[0].2);
|
||||
assert!(!r[0].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1621,7 +1626,7 @@ mod tests {
|
|||
&[("MyModule", Middleware::ModuleScript)],
|
||||
);
|
||||
assert_eq!(r[0].1, "MyModule~2.luau");
|
||||
assert!(r[0].2, "dedup suffix means needs meta");
|
||||
assert!(!r[0].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1679,7 +1684,7 @@ mod tests {
|
|||
// New "Foo" dir must skip both and land on "Foo~2".
|
||||
let r = process_new_children_with_disk_seed(&["foo", "foo~1"], &[("Foo", Middleware::Dir)]);
|
||||
assert_eq!(r[0].1, "Foo~2");
|
||||
assert!(r[0].2);
|
||||
assert!(!r[0].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1776,9 +1781,9 @@ mod tests {
|
|||
&[("Helper", Middleware::Dir), ("Helper", Middleware::Dir)],
|
||||
);
|
||||
assert_eq!(r[0].1, "Helper~2");
|
||||
assert!(r[0].2);
|
||||
assert!(!r[0].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[1].1, "Helper~3");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1796,11 +1801,11 @@ mod tests {
|
|||
assert_eq!(r[0].1, "Test");
|
||||
assert!(!r[0].2);
|
||||
assert_eq!(r[1].1, "test~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[2].1, "TEST~3");
|
||||
assert!(r[2].2);
|
||||
assert!(!r[2].2, "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[3].1, "tEsT~4");
|
||||
assert!(r[3].2);
|
||||
assert!(!r[3].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════
|
||||
|
|
@ -1873,7 +1878,7 @@ mod tests {
|
|||
assert_eq!(r[0].1, "🎮 Games");
|
||||
assert!(!r[0].2, "emoji name is clean");
|
||||
assert_eq!(r[1].1, "🎮 Games~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1884,7 +1889,7 @@ mod tests {
|
|||
assert_eq!(r[0].1, "CON_");
|
||||
assert!(r[0].2);
|
||||
assert_eq!(r[1].1, "CON_~2");
|
||||
assert!(r[1].2, "natural CON_ collides with reserved CON's slug");
|
||||
assert!(!r[1].2, "dedup-only: natural CON_ needs no meta");
|
||||
assert_eq!(r[2].1, "con_~3");
|
||||
assert!(r[2].2);
|
||||
}
|
||||
|
|
@ -1896,7 +1901,7 @@ mod tests {
|
|||
assert_eq!(r[0].1, "test");
|
||||
assert!(r[0].2, "trailing dot stripped → slug differs");
|
||||
assert_eq!(r[1].1, "test~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: clean name, forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1906,9 +1911,9 @@ mod tests {
|
|||
assert_eq!(r[0].1, "A");
|
||||
assert!(r[0].2, "leading space stripped");
|
||||
assert_eq!(r[1].1, "A~2");
|
||||
assert!(r[1].2);
|
||||
assert!(!r[1].2, "dedup-only: clean name, forward sync strips ~N");
|
||||
assert_eq!(r[2].1, "A~3");
|
||||
assert!(r[2].2);
|
||||
assert!(r[2].2, "leading spaces stripped → slug differs");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2067,7 +2072,7 @@ mod tests {
|
|||
assert_eq!(r[1].1, "a_project~2");
|
||||
assert!(r[1].2);
|
||||
assert_eq!(r[2].1, "a_project~3");
|
||||
assert!(r[2].2, "natural name collides with slugified siblings");
|
||||
assert!(!r[2].2, "dedup-only: natural name, forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2079,7 +2084,7 @@ mod tests {
|
|||
assert!(!r[0].2);
|
||||
for (i, entry) in r.iter().enumerate().skip(1) {
|
||||
assert_eq!(entry.1, format!("Script~{}", i + 1));
|
||||
assert!(entry.2);
|
||||
assert!(!entry.2, "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
// All filenames must be unique
|
||||
let unique: HashSet<String> = r.iter().map(|(_, f, _)| f.to_lowercase()).collect();
|
||||
|
|
@ -2333,17 +2338,15 @@ mod tests {
|
|||
assert_eq!(name1.as_ref(), "A_B.luau");
|
||||
assert!(meta1, "A/B was slugified, needs meta name");
|
||||
assert_eq!(name2.as_ref(), "A_B~2.luau");
|
||||
assert!(meta2, "A_B was deduped to ~2, needs meta name");
|
||||
assert!(!meta2, "dedup-only: natural A_B, forward sync strips ~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tilde_in_filename_not_parsed_as_dedup_marker() {
|
||||
// A file named "Foo~1" should produce instance name "Foo~1",
|
||||
// NOT be interpreted as "Foo" with dedup suffix ~1.
|
||||
// This is verified by the fact that name_needs_slugify("Foo~1")
|
||||
// returns true (tilde is in SLUGIFY_CHARS), so a file named
|
||||
// "Foo~1.luau" can only exist if it was written by the dedup
|
||||
// system. Forward sync reads the filename as-is.
|
||||
// An instance literally named "Foo~1" gets slugified to "Foo_1"
|
||||
// because tilde is in SLUGIFY_CHARS. This means a file named
|
||||
// "Foo~1.luau" on disk can only have been produced by the dedup
|
||||
// system, so forward sync correctly strips ~1 to derive "Foo".
|
||||
assert!(
|
||||
name_needs_slugify("Foo~1"),
|
||||
"tilde should trigger slugification"
|
||||
|
|
|
|||
|
|
@ -1929,7 +1929,7 @@ impl ApiService {
|
|||
// not filenames with extensions). This ensures file-format instances
|
||||
// are correctly detected as collisions.
|
||||
let encoded_name = deduplicate_name(&base_name, sibling_slugs);
|
||||
let needs_meta_name = needs_slug || encoded_name != base_name;
|
||||
let needs_meta_name = needs_slug;
|
||||
let meta_name_field: Option<&str> = if needs_meta_name {
|
||||
Some(&added.name)
|
||||
} else {
|
||||
|
|
@ -6478,7 +6478,7 @@ mod tests {
|
|||
instance_name.to_string()
|
||||
};
|
||||
let deduped = deduplicate_name(&base, &taken);
|
||||
let needs_meta = needs_slug || deduped != base;
|
||||
let needs_meta = needs_slug;
|
||||
|
||||
fs::create_dir(temp.path().join(&deduped)).unwrap();
|
||||
|
||||
|
|
@ -6516,7 +6516,7 @@ mod tests {
|
|||
name.to_string()
|
||||
};
|
||||
let deduped = deduplicate_name(&base, &taken);
|
||||
let needs_meta = needs_slug || deduped != base;
|
||||
let needs_meta = needs_slug;
|
||||
taken.insert(deduped.to_lowercase());
|
||||
let meta = if needs_meta {
|
||||
Some(name.to_string())
|
||||
|
|
@ -6541,7 +6541,7 @@ mod tests {
|
|||
fn dir_clean_name_collides_with_existing() {
|
||||
let (f, m) = simulate_dir_dedup(&["NewFolder"], "NewFolder");
|
||||
assert_eq!(f, "NewFolder~2");
|
||||
assert_eq!(m.unwrap(), "NewFolder");
|
||||
assert!(m.is_none(), "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -6562,14 +6562,14 @@ mod tests {
|
|||
fn dir_natural_collides_with_slug() {
|
||||
let (f, m) = simulate_dir_dedup(&["A_B"], "A_B");
|
||||
assert_eq!(f, "A_B~2");
|
||||
assert_eq!(m.unwrap(), "A_B");
|
||||
assert!(m.is_none(), "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dir_case_insensitive_collision() {
|
||||
let (f, m) = simulate_dir_dedup(&["MyFolder"], "myfolder");
|
||||
assert_eq!(f, "myfolder~2");
|
||||
assert_eq!(m.unwrap(), "myfolder");
|
||||
assert!(m.is_none(), "dedup-only: forward sync strips ~N");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -6628,7 +6628,7 @@ mod tests {
|
|||
assert_eq!(r[0].0, "Script");
|
||||
assert!(r[0].1.is_none());
|
||||
assert_eq!(r[1].0, "Script~2");
|
||||
assert_eq!(r[1].1.as_deref(), Some("Script"));
|
||||
assert!(r[1].1.is_none(), "dedup-only: forward sync strips ~N");
|
||||
assert_eq!(r[2].0, "Script~3");
|
||||
}
|
||||
|
||||
|
|
@ -6663,7 +6663,7 @@ mod tests {
|
|||
assert_eq!(r[0].0, "CON_");
|
||||
assert_eq!(r[0].1.as_deref(), Some("CON"));
|
||||
assert_eq!(r[1].0, "CON_~2");
|
||||
assert_eq!(r[1].1.as_deref(), Some("CON_"));
|
||||
assert!(r[1].1.is_none(), "dedup-only: natural CON_ needs no meta");
|
||||
assert_eq!(r[2].0, "con_~3");
|
||||
assert_eq!(r[2].1.as_deref(), Some("con"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue