Skip to content

Commit 74da3b2

Browse files
committed
fix(criterion-compat): fix URI formatting when criterion_group!/criterion_main! are bypassed
1 parent dccf5f4 commit 74da3b2

File tree

4 files changed

+65
-7
lines changed

4 files changed

+65
-7
lines changed

crates/criterion_compat/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@ harness = false
5656
[[bench]]
5757
name = "test_benches"
5858
harness = false
59+
60+
[[bench]]
61+
name = "repro_custom_main"
62+
harness = false
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// Reproducer for COD-2324: URI formatting issues when users bypass criterion_group!/criterion_main!
2+
/// and use a custom main function (like the rtmalloc project does).
3+
use codspeed_criterion_compat::{criterion_group, BenchmarkId, Criterion};
4+
5+
fn bench_with_group(c: &mut Criterion) {
6+
let mut group = c.benchmark_group("my_group");
7+
group.bench_function("my_bench", |b| b.iter(|| 2 + 2));
8+
group.bench_function(BenchmarkId::new("parameterized", 42), |b| b.iter(|| 2 + 2));
9+
group.finish();
10+
}
11+
12+
// Scenario 1: criterion_group! defined but NOT used — custom main calls bench functions directly
13+
criterion_group!(benches, bench_with_group);
14+
15+
fn main() {
16+
// Pattern A: Using new_instrumented() but calling bench functions directly (not through criterion_group!)
17+
let mut criterion = Criterion::new_instrumented();
18+
bench_with_group(&mut criterion);
19+
20+
// Pattern B: Calling through criterion_group!-generated function (should work correctly)
21+
let mut criterion2 = Criterion::new_instrumented();
22+
benches(&mut criterion2);
23+
}

crates/criterion_compat/src/compat/criterion.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,23 @@ impl<M: Measurement> Criterion<M> {
9292
self.macro_group = macro_group.into();
9393
}
9494

95+
#[track_caller]
9596
pub fn bench_function<F>(&mut self, id: &str, f: F) -> &mut Criterion<M>
9697
where
9798
F: FnMut(&mut Bencher),
9899
{
100+
self.fill_missing_file_from_caller();
99101
self.benchmark_group(id)
100102
.bench_function(BenchmarkId::no_function(), f);
101103
self
102104
}
103105

106+
#[track_caller]
104107
pub fn bench_with_input<F, I>(&mut self, id: BenchmarkId, input: &I, f: F) -> &mut Criterion<M>
105108
where
106109
F: FnMut(&mut Bencher, &I),
107110
{
111+
self.fill_missing_file_from_caller();
108112
let group_name = id.function_name.expect(
109113
"Cannot use BenchmarkId::from_parameter with Criterion::bench_with_input. \
110114
Consider using a BenchmarkGroup or BenchmarkId::new instead.",
@@ -118,9 +122,28 @@ impl<M: Measurement> Criterion<M> {
118122
self
119123
}
120124

125+
#[track_caller]
121126
pub fn benchmark_group<S: Into<String>>(&mut self, group_name: S) -> BenchmarkGroup<M> {
127+
self.fill_missing_file_from_caller();
122128
BenchmarkGroup::<M>::new(self, group_name.into())
123129
}
130+
131+
/// When `current_file` wasn't set by `criterion_group!`, derive it from the caller location.
132+
#[track_caller]
133+
fn fill_missing_file_from_caller(&mut self) {
134+
if !self.current_file.is_empty() {
135+
return;
136+
}
137+
let caller = std::panic::Location::caller();
138+
if let Ok(workspace_root) = std::env::var("CODSPEED_CARGO_WORKSPACE_ROOT") {
139+
self.current_file = std::path::PathBuf::from(workspace_root)
140+
.join(caller.file())
141+
.to_string_lossy()
142+
.into_owned();
143+
} else {
144+
self.current_file = caller.file().to_string();
145+
}
146+
}
124147
}
125148

126149
// Dummy methods

crates/criterion_compat/src/compat/group.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,21 @@ impl<'a, M: Measurement> BenchmarkGroup<'a, M> {
6262
F: FnMut(&mut Bencher, &I),
6363
I: ?Sized,
6464
{
65-
let git_relative_file_path = get_git_relative_path(&self.current_file);
66-
let mut uri = format!(
67-
"{}::{}::{}",
68-
git_relative_file_path.to_string_lossy(),
69-
self.macro_group,
70-
self.group_name,
71-
);
65+
let mut uri_parts: Vec<String> = Vec::new();
66+
67+
if !self.current_file.is_empty() {
68+
let git_relative_file_path = get_git_relative_path(&self.current_file);
69+
let file_str = git_relative_file_path.to_string_lossy();
70+
if !file_str.is_empty() {
71+
uri_parts.push(file_str.into_owned());
72+
}
73+
}
74+
if !self.macro_group.is_empty() {
75+
uri_parts.push(self.macro_group.clone());
76+
}
77+
uri_parts.push(self.group_name.clone());
78+
79+
let mut uri = uri_parts.join("::");
7280
if let Some(function_name) = id.function_name {
7381
uri = format!("{uri}::{function_name}");
7482
}

0 commit comments

Comments
 (0)