Skip to content

Commit 6a55b59

Browse files
Brooooooklynclaude
andauthored
test: add missing tests and benchmarks for functions used in rolldown (#24)
Add comprehensive test coverage and benchmarks for SugarPath methods that are heavily used in the rolldown codebase but lacked proper testing: - as_path() - 53 occurrences in rolldown, now with full test suite - absolutize_with() - 4 occurrences, added dedicated tests - relative() - 31 occurrences, added benchmarks - to_slash()/to_slash_lossy() - 24 total occurrences, added benchmarks This ensures the most critical functionality used by downstream projects has proper test coverage and performance benchmarks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 17730b2 commit 6a55b59

File tree

6 files changed

+548
-0
lines changed

6 files changed

+548
-0
lines changed

Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,15 @@ harness = false
2727
[[bench]]
2828
name = "normalize"
2929
harness = false
30+
31+
[[bench]]
32+
name = "relative"
33+
harness = false
34+
35+
[[bench]]
36+
name = "to_slash"
37+
harness = false
38+
39+
[[bench]]
40+
name = "as_path"
41+
harness = false

benches/as_path.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use std::hint::black_box;
2+
use std::path::Path;
3+
4+
use criterion::{Criterion, criterion_group, criterion_main};
5+
use sugar_path::SugarPath;
6+
7+
mod fixtures;
8+
9+
use fixtures::FIXTURES;
10+
11+
fn criterion_benchmark(c: &mut Criterion) {
12+
c.bench_function("as_path_str", |b| {
13+
b.iter(|| {
14+
for fixture in FIXTURES {
15+
// Benchmark converting &str to Path using as_path
16+
let path = black_box(fixture.as_path());
17+
black_box(path);
18+
}
19+
})
20+
});
21+
22+
c.bench_function("as_path_string", |b| {
23+
let string_fixtures: Vec<String> = FIXTURES.iter().map(|s| s.to_string()).collect();
24+
b.iter(|| {
25+
for fixture in &string_fixtures {
26+
// Benchmark converting String to Path using as_path
27+
let path = black_box(fixture.as_path());
28+
black_box(path);
29+
}
30+
})
31+
});
32+
33+
c.bench_function("as_path_vs_path_new", |b| {
34+
b.iter(|| {
35+
for fixture in FIXTURES {
36+
// Compare as_path() with Path::new() for baseline
37+
let path1 = black_box(fixture.as_path());
38+
let path2 = black_box(Path::new(fixture));
39+
black_box((path1, path2));
40+
}
41+
})
42+
});
43+
44+
c.bench_function("as_path_chaining", |b| {
45+
b.iter(|| {
46+
for fixture in FIXTURES {
47+
// Benchmark as_path followed by other operations
48+
let normalized = black_box(fixture.as_path().normalize());
49+
black_box(normalized);
50+
}
51+
})
52+
});
53+
}
54+
55+
criterion_group!(benches, criterion_benchmark);
56+
criterion_main!(benches);

benches/relative.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use std::hint::black_box;
2+
use std::path::Path;
3+
4+
use criterion::{Criterion, criterion_group, criterion_main};
5+
use sugar_path::SugarPath;
6+
7+
fn criterion_benchmark(c: &mut Criterion) {
8+
// Common test cases for relative path computation
9+
let test_cases = vec![
10+
("/var/lib", "/var"),
11+
("/var/lib", "/bin"),
12+
("/var/lib", "/var/lib"),
13+
("/var/lib", "/var/apache"),
14+
("/var/", "/var/lib"),
15+
("/", "/var/lib"),
16+
("/foo/test", "/foo/test/bar/package.json"),
17+
("/Users/a/web/b/test/mails", "/Users/a/web/b"),
18+
("/foo/bar/baz-quux", "/foo/bar/baz"),
19+
("/foo/bar/baz", "/foo/bar/baz-quux"),
20+
("/home/user/documents", "/home/user/downloads"),
21+
("/usr/local/bin", "/usr/share/doc"),
22+
("/a/b/c/d/e", "/a/b/f/g/h"),
23+
];
24+
25+
c.bench_function("relative_simple", |b| {
26+
b.iter(|| {
27+
for (base, target) in &test_cases {
28+
let result = black_box(Path::new(target).relative(base));
29+
black_box(result);
30+
}
31+
})
32+
});
33+
34+
c.bench_function("relative_deep_nesting", |b| {
35+
let deep_cases = vec![
36+
("/a/b/c/d/e/f/g", "/a/b/c/d/e/f/h"),
37+
("/a/b/c/d/e/f/g", "/x/y/z"),
38+
("/very/long/path/to/some/deeply/nested/directory", "/very/long/path/to/another/directory"),
39+
(
40+
"/usr/local/lib/python3.9/site-packages/numpy",
41+
"/usr/local/lib/python3.9/site-packages/pandas",
42+
),
43+
];
44+
45+
b.iter(|| {
46+
for (base, target) in &deep_cases {
47+
let result = black_box(Path::new(target).relative(base));
48+
black_box(result);
49+
}
50+
})
51+
});
52+
53+
c.bench_function("relative_with_dots", |b| {
54+
let dot_cases = vec![
55+
("/var/../usr/lib", "/var/../usr/bin"),
56+
("/home/./user/../user/docs", "/home/user/downloads"),
57+
("/a/b/../c/d", "/a/b/../c/e"),
58+
];
59+
60+
b.iter(|| {
61+
for (base, target) in &dot_cases {
62+
let normalized_base = Path::new(base).normalize();
63+
let normalized_target = Path::new(target).normalize();
64+
let result = black_box(normalized_target.relative(&normalized_base));
65+
black_box(result);
66+
}
67+
})
68+
});
69+
70+
#[cfg(target_family = "windows")]
71+
c.bench_function("relative_windows_paths", |b| {
72+
let windows_cases = vec![
73+
("C:\\Users\\Admin\\Documents", "C:\\Users\\Admin\\Downloads"),
74+
("C:\\Windows\\System32", "C:\\Program Files"),
75+
("D:\\Projects\\rust", "D:\\Projects\\python"),
76+
("\\\\server\\share\\folder", "\\\\server\\share\\file"),
77+
];
78+
79+
b.iter(|| {
80+
for (base, target) in &windows_cases {
81+
let result = black_box(Path::new(target).relative(base));
82+
black_box(result);
83+
}
84+
})
85+
});
86+
87+
c.bench_function("relative_same_path", |b| {
88+
let paths = vec!["/home/user/documents", "/var/log/system", "/usr/local/bin"];
89+
90+
b.iter(|| {
91+
for path in &paths {
92+
// Benchmark relative when base and target are the same
93+
let result = black_box(Path::new(path).relative(path));
94+
black_box(result);
95+
}
96+
})
97+
});
98+
99+
c.bench_function("relative_parent_child", |b| {
100+
let parent_child = vec![
101+
("/parent", "/parent/child"),
102+
("/parent/child", "/parent"),
103+
("/a/b", "/a/b/c/d"),
104+
("/a/b/c/d", "/a/b"),
105+
];
106+
107+
b.iter(|| {
108+
for (base, target) in &parent_child {
109+
let result = black_box(Path::new(target).relative(base));
110+
black_box(result);
111+
}
112+
})
113+
});
114+
}
115+
116+
criterion_group!(benches, criterion_benchmark);
117+
criterion_main!(benches);

benches/to_slash.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
use std::hint::black_box;
2+
use std::path::Path;
3+
4+
use criterion::{Criterion, criterion_group, criterion_main};
5+
use sugar_path::SugarPath;
6+
7+
mod fixtures;
8+
9+
use fixtures::{ABSOLUTE_PATHS, FIXTURES};
10+
11+
fn criterion_benchmark(c: &mut Criterion) {
12+
c.bench_function("to_slash", |b| {
13+
b.iter(|| {
14+
for fixture in FIXTURES {
15+
let path = Path::new(fixture);
16+
let result = black_box(path.to_slash());
17+
black_box(result);
18+
}
19+
})
20+
});
21+
22+
c.bench_function("to_slash_lossy", |b| {
23+
b.iter(|| {
24+
for fixture in FIXTURES {
25+
let path = Path::new(fixture);
26+
let result = black_box(path.to_slash_lossy());
27+
black_box(result);
28+
}
29+
})
30+
});
31+
32+
c.bench_function("to_slash_absolute_paths", |b| {
33+
b.iter(|| {
34+
for path_str in ABSOLUTE_PATHS {
35+
let path = Path::new(path_str);
36+
let result = black_box(path.to_slash());
37+
black_box(result);
38+
}
39+
})
40+
});
41+
42+
c.bench_function("to_slash_lossy_absolute_paths", |b| {
43+
b.iter(|| {
44+
for path_str in ABSOLUTE_PATHS {
45+
let path = Path::new(path_str);
46+
let result = black_box(path.to_slash_lossy());
47+
black_box(result);
48+
}
49+
})
50+
});
51+
52+
#[cfg(target_family = "windows")]
53+
c.bench_function("to_slash_windows_specific", |b| {
54+
let windows_paths = vec![
55+
"C:\\Windows\\System32",
56+
"C:\\Users\\Admin\\Documents\\file.txt",
57+
"D:\\Projects\\rust\\src\\main.rs",
58+
"\\\\server\\share\\folder\\document.doc",
59+
"C:\\Program Files\\Application\\bin",
60+
"C:\\temp\\cache\\..\\data",
61+
"file:stream",
62+
"C:relative\\path",
63+
];
64+
65+
b.iter(|| {
66+
for path_str in &windows_paths {
67+
let path = Path::new(path_str);
68+
let result = black_box(path.to_slash());
69+
black_box(result);
70+
}
71+
})
72+
});
73+
74+
#[cfg(target_family = "windows")]
75+
c.bench_function("to_slash_lossy_windows_specific", |b| {
76+
let windows_paths = vec![
77+
"C:\\Windows\\System32",
78+
"C:\\Users\\Admin\\Documents\\file.txt",
79+
"D:\\Projects\\rust\\src\\main.rs",
80+
"\\\\server\\share\\folder\\document.doc",
81+
"C:\\Program Files\\Application\\bin",
82+
"C:\\temp\\cache\\..\\data",
83+
"file:stream",
84+
"C:relative\\path",
85+
];
86+
87+
b.iter(|| {
88+
for path_str in &windows_paths {
89+
let path = Path::new(path_str);
90+
let result = black_box(path.to_slash_lossy());
91+
black_box(result);
92+
}
93+
})
94+
});
95+
96+
c.bench_function("to_slash_mixed_separators", |b| {
97+
let mixed_paths =
98+
vec!["foo/bar\\baz", "hello\\world/test", "./foo\\../bar/baz", "C:/Users\\Admin/Documents"];
99+
100+
b.iter(|| {
101+
for path_str in &mixed_paths {
102+
let path = Path::new(path_str);
103+
let result = black_box(path.to_slash());
104+
black_box(result);
105+
}
106+
})
107+
});
108+
109+
c.bench_function("to_slash_deep_nesting", |b| {
110+
let deep_paths = vec![
111+
"a/b/c/d/e/f/g/h/i/j/k/l/m/n",
112+
"/usr/local/lib/python3.9/site-packages/numpy/core/include",
113+
"./very/long/relative/path/to/some/deeply/nested/file.txt",
114+
];
115+
116+
b.iter(|| {
117+
for path_str in &deep_paths {
118+
let path = Path::new(path_str);
119+
let result = black_box(path.to_slash());
120+
black_box(result);
121+
}
122+
})
123+
});
124+
125+
c.bench_function("to_slash_vs_to_slash_lossy", |b| {
126+
b.iter(|| {
127+
for fixture in FIXTURES {
128+
let path = Path::new(fixture);
129+
let result1 = black_box(path.to_slash());
130+
let result2 = black_box(path.to_slash_lossy());
131+
black_box((result1, result2));
132+
}
133+
})
134+
});
135+
}
136+
137+
criterion_group!(benches, criterion_benchmark);
138+
criterion_main!(benches);

0 commit comments

Comments
 (0)