Skip to content

Commit 98e1044

Browse files
authored
test: add deep paths fixtures (#25)
1 parent 6a55b59 commit 98e1044

File tree

3 files changed

+231
-0
lines changed

3 files changed

+231
-0
lines changed

benches/fixtures.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,29 @@ pub static FIXTURES: &[&str] = &[
2525
"a/b/c/../../../",
2626
"a/b/c/../../..",
2727
"",
28+
// Deep paths (8 components - at SmallVec boundary)
29+
"a/b/c/d/e/f/g/h",
30+
"/level1/level2/level3/level4/level5/level6/level7/level8",
31+
// Deep paths (9-12 components - just over SmallVec inline capacity)
32+
"a/b/c/d/e/f/g/h/i",
33+
"/level1/level2/level3/level4/level5/level6/level7/level8/level9",
34+
"comp1/comp2/comp3/comp4/comp5/comp6/comp7/comp8/comp9/comp10",
35+
"/root/sub1/sub2/sub3/sub4/sub5/sub6/sub7/sub8/sub9/sub10/file.txt",
36+
// Deep paths (15+ components)
37+
"a/b/c/d/e/f/g/h/i/j/k/l/m/n/o",
38+
"/level1/level2/level3/level4/level5/level6/level7/level8/level9/level10/level11/level12/level13/level14/level15",
39+
// Very deep paths (20+ components)
40+
"a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t",
41+
"/home/user/projects/company/backend/services/api/controllers/v2/handlers/auth/login/validate/token/refresh/generate/new",
42+
// Deep paths with dots for normalization
43+
"a/./b/./c/./d/./e/./f/./g/./h/./i/./j",
44+
"/level1/./level2/./level3/./level4/./level5/./level6/./level7/./level8/./level9",
45+
"a/b/../c/d/../e/f/../g/h/../i/j/../k/l/../m/n/../o/p",
46+
"/level1/level2/../level3/level4/../level5/level6/../level7/level8/../level9/level10",
47+
// Complex normalization with deep nesting
48+
"a/./b/../c/./d/../e/./f/../g/./h/../i/./j/../k/./l/../m/./n/../o/./p",
49+
"../../../a/b/c/../../d/e/f/../../g/h/i/../../j/k/l/../../m/n/o",
50+
"./a/b/./c/../d/./e/f/../../g/h/./i/../j/./k/l/../../m/./n/o/../p/q/./r",
2851
];
2952

3053
#[allow(dead_code)]
@@ -50,4 +73,9 @@ pub static ABSOLUTE_PATHS: &[&str] = &[
5073
"/usr/bin/python3",
5174
"/var/spool/mail",
5275
"/opt/tools/scripts/deploy.sh",
76+
// Deep absolute paths
77+
"/level1/level2/level3/level4/level5/level6/level7/level8/level9/level10/level11/level12",
78+
"/usr/local/share/doc/packages/example/tutorials/advanced/chapter1/section2/subsection3/page4.html",
79+
"/home/user/workspace/projects/company/backend/microservices/auth-service/src/controllers/v2/handlers/login.js",
80+
"/var/log/applications/production/cluster-01/node-03/services/api-gateway/2024/01/15/access.log",
5381
];

benches/relative.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,28 @@ fn criterion_benchmark(c: &mut Criterion) {
3333

3434
c.bench_function("relative_deep_nesting", |b| {
3535
let deep_cases = vec![
36+
// Original cases (7-8 components)
3637
("/a/b/c/d/e/f/g", "/a/b/c/d/e/f/h"),
3738
("/a/b/c/d/e/f/g", "/x/y/z"),
3839
("/very/long/path/to/some/deeply/nested/directory", "/very/long/path/to/another/directory"),
3940
(
4041
"/usr/local/lib/python3.9/site-packages/numpy",
4142
"/usr/local/lib/python3.9/site-packages/pandas",
4243
),
44+
// 8 components - at SmallVec boundary
45+
("/level1/level2/level3/level4/level5/level6/level7/level8",
46+
"/level1/level2/level3/level4/level5/level6/level7/different8"),
47+
// 10 components - just over SmallVec boundary
48+
("/a/b/c/d/e/f/g/h/i/j", "/a/b/c/d/e/f/g/h/x/y"),
49+
// 15 components - well over SmallVec boundary
50+
("/root/sub1/sub2/sub3/sub4/sub5/sub6/sub7/sub8/sub9/sub10/sub11/sub12/sub13/sub14",
51+
"/root/sub1/sub2/sub3/sub4/sub5/sub6/sub7/sub8/sub9/sub10/sub11/different12/different13/different14"),
52+
// 20 components - extreme depth
53+
("/home/user/projects/company/backend/services/api/controllers/v2/handlers/auth/login/validate/token/refresh/generate/key/store/cache",
54+
"/home/user/projects/company/backend/services/api/controllers/v2/handlers/auth/login/validate/token/refresh/generate/key/fetch/remote"),
55+
// Different common prefix depths with deep paths
56+
("/level1/level2/level3/level4/level5/level6/level7/level8/level9/level10/unique1/unique2",
57+
"/level1/level2/level3/level4/different5/different6/different7/different8/different9/different10/different11/different12"),
4358
];
4459

4560
b.iter(|| {

tests/deep_paths.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
//! Tests for paths with many components to verify SmallVec spills to heap correctly
2+
3+
use std::path::Path;
4+
use sugar_path::SugarPath;
5+
6+
#[test]
7+
fn test_normalize_deep_path() {
8+
// Create a path with more than 8 components (SmallVec inline capacity)
9+
let deep_path = "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p";
10+
let normalized = deep_path.normalize();
11+
assert_eq!(normalized, Path::new(deep_path));
12+
13+
// Test with dots
14+
let deep_path_with_dots = "a/b/c/./d/e/f/./g/h/i/./j/k/l/./m/n/o/p";
15+
let normalized = deep_path_with_dots.normalize();
16+
assert_eq!(normalized, Path::new("a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p"));
17+
18+
// Test with parent dirs
19+
let deep_path_with_parents = "a/b/c/../d/e/f/../g/h/i/../j/k/l/../m/n/o/p";
20+
let normalized = deep_path_with_parents.normalize();
21+
assert_eq!(normalized, Path::new("a/b/d/e/g/h/j/k/m/n/o/p"));
22+
}
23+
24+
#[test]
25+
fn test_relative_deep_paths() {
26+
// Test relative path calculation with more than 8 components
27+
let base = "/a/b/c/d/e/f/g/h/i/j";
28+
let target = "/a/b/c/d/e/f/g/k/l/m/n/o/p";
29+
30+
let relative = target.relative(base);
31+
assert_eq!(relative, Path::new("../../../k/l/m/n/o/p"));
32+
33+
// Test with even deeper paths (15+ components)
34+
let base =
35+
"/level1/level2/level3/level4/level5/level6/level7/level8/level9/level10/level11/level12";
36+
let target = "/level1/level2/level3/level4/level5/level6/level7/level8/different9/different10/different11/different12/different13/different14/different15";
37+
38+
let relative = target.relative(base);
39+
assert_eq!(
40+
relative,
41+
Path::new(
42+
"../../../../different9/different10/different11/different12/different13/different14/different15"
43+
)
44+
);
45+
}
46+
47+
#[test]
48+
fn test_absolutize_deep_paths() {
49+
// Test absolutize with deep paths
50+
#[cfg(target_family = "unix")]
51+
{
52+
let base = "/root/level1/level2/level3/level4/level5/level6/level7/level8/level9";
53+
let relative = "../../../../../../../../../../deep1/deep2/deep3/deep4/deep5";
54+
55+
let absolute = relative.absolutize_with(base);
56+
assert_eq!(absolute, Path::new("/deep1/deep2/deep3/deep4/deep5"));
57+
58+
// Test with current directory dots in deep path
59+
let deep_relative = "./sub1/./sub2/./sub3/./sub4/./sub5/./sub6/./sub7/./sub8/./sub9/./sub10";
60+
let absolute = deep_relative.absolutize_with(base);
61+
assert_eq!(
62+
absolute,
63+
Path::new(
64+
"/root/level1/level2/level3/level4/level5/level6/level7/level8/level9/sub1/sub2/sub3/sub4/sub5/sub6/sub7/sub8/sub9/sub10"
65+
)
66+
);
67+
}
68+
69+
#[cfg(target_family = "windows")]
70+
{
71+
let base = "C:\\root\\level1\\level2\\level3\\level4\\level5\\level6\\level7\\level8\\level9";
72+
let relative = "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\deep1\\deep2\\deep3\\deep4\\deep5";
73+
74+
let absolute = relative.absolutize_with(base);
75+
assert_eq!(absolute, Path::new("C:\\deep1\\deep2\\deep3\\deep4\\deep5"));
76+
77+
// Test with current directory dots in deep path
78+
let deep_relative =
79+
".\\sub1\\.\\sub2\\.\\sub3\\.\\sub4\\.\\sub5\\.\\sub6\\.\\sub7\\.\\sub8\\.\\sub9\\.\\sub10";
80+
let absolute = deep_relative.absolutize_with(base);
81+
assert_eq!(
82+
absolute,
83+
Path::new(
84+
"C:\\root\\level1\\level2\\level3\\level4\\level5\\level6\\level7\\level8\\level9\\sub1\\sub2\\sub3\\sub4\\sub5\\sub6\\sub7\\sub8\\sub9\\sub10"
85+
)
86+
);
87+
}
88+
}
89+
90+
#[test]
91+
fn test_to_slash_deep_paths() {
92+
// Test to_slash with deep paths
93+
#[cfg(target_family = "windows")]
94+
{
95+
let deep_path = Path::new("a\\b\\c\\d\\e\\f\\g\\h\\i\\j\\k\\l\\m\\n\\o\\p");
96+
let slashed = deep_path.to_slash().unwrap();
97+
assert_eq!(slashed, "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p");
98+
}
99+
100+
#[cfg(target_family = "unix")]
101+
{
102+
let deep_path = Path::new("a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p");
103+
let slashed = deep_path.to_slash().unwrap();
104+
assert_eq!(slashed, "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p");
105+
}
106+
}
107+
108+
#[test]
109+
fn test_extreme_depth() {
110+
// Test with 20+ components
111+
let mut path_parts = Vec::new();
112+
for i in 0..25 {
113+
path_parts.push(format!("component{}", i));
114+
}
115+
let deep_path = path_parts.join("/");
116+
117+
// Test normalize
118+
let normalized = deep_path.as_str().normalize();
119+
assert_eq!(normalized.components().count(), 25);
120+
121+
// Add some dots and test again
122+
let mut path_with_dots = Vec::new();
123+
for i in 0..25 {
124+
path_with_dots.push(format!("component{}", i));
125+
if i % 3 == 0 {
126+
path_with_dots.push(".".to_string());
127+
}
128+
}
129+
let deep_path_dots = path_with_dots.join("/");
130+
let normalized = deep_path_dots.as_str().normalize();
131+
assert_eq!(normalized.components().count(), 25);
132+
}
133+
134+
#[test]
135+
fn test_stress_smallvec_spillover() {
136+
// Create paths that will definitely spill over SmallVec's inline capacity
137+
138+
// Test 1: Exactly at boundary (8 components)
139+
let path8 = "a/b/c/d/e/f/g/h";
140+
let normalized8 = path8.normalize();
141+
assert_eq!(normalized8.components().count(), 8);
142+
143+
// Test 2: Just over boundary (9 components)
144+
let path9 = "a/b/c/d/e/f/g/h/i";
145+
let normalized9 = path9.normalize();
146+
assert_eq!(normalized9.components().count(), 9);
147+
148+
// Test 3: Well over boundary (16 components)
149+
let path16 = "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p";
150+
let normalized16 = path16.normalize();
151+
assert_eq!(normalized16.components().count(), 16);
152+
153+
// Test 4: Complex normalization with many components
154+
let complex = "a/./b/../c/d/./e/../f/g/./h/../i/j/./k/../l/m/./n/../o/p/./q/../r";
155+
let normalized_complex = complex.normalize();
156+
// This should normalize to: a/c/d/f/g/i/j/l/m/o/p/r (12 components)
157+
assert_eq!(normalized_complex, Path::new("a/c/d/f/g/i/j/l/m/o/p/r"));
158+
}
159+
160+
#[test]
161+
fn test_windows_deep_paths() {
162+
#[cfg(target_family = "windows")]
163+
{
164+
// Test Windows-specific deep path handling
165+
let deep_win_path = "C:\\level1\\level2\\level3\\level4\\level5\\level6\\level7\\level8\\level9\\level10\\level11\\level12";
166+
let normalized = deep_win_path.normalize();
167+
assert!(normalized.components().count() > 8);
168+
169+
// Test UNC paths with many components
170+
let unc_path = "\\\\server\\share\\folder1\\folder2\\folder3\\folder4\\folder5\\folder6\\folder7\\folder8\\folder9\\folder10";
171+
let normalized_unc = unc_path.normalize();
172+
assert!(normalized_unc.components().count() > 8);
173+
}
174+
}
175+
176+
#[test]
177+
fn test_relative_with_common_deep_prefix() {
178+
// Test paths that share a deep common prefix
179+
let base = "/shared/path/components/that/are/very/deep/and/long/base/specific/part";
180+
let target = "/shared/path/components/that/are/very/deep/and/long/target/different/end";
181+
182+
let relative = target.relative(base);
183+
assert_eq!(relative, Path::new("../../../target/different/end"));
184+
185+
// Verify the path has the expected structure
186+
let components: Vec<_> = relative.components().collect();
187+
assert_eq!(components.len(), 6);
188+
}

0 commit comments

Comments
 (0)