use std::hint::black_box;
use apimock_routing::ParsedRequest;
use apimock_routing::RuleSet;
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use hyper::Request;
fn build_rule_set(rule_count: usize) -> (tempfile::TempDir, RuleSet) {
let dir = tempfile::tempdir().expect("tempdir");
let rule_set_path = dir.path().join("rules.toml");
let mut toml = String::new();
toml.push_str("[prefix]\nurl_path = \"/api\"\n\n");
for i in 0..rule_count {
match i % 3 {
0 => toml.push_str(&format!(
"[[rules]]\nwhen.request.url_path = \"/v1/users/{}\"\nrespond = {{ text = \"user-{}\" }}\n\n",
i, i,
)),
1 => toml.push_str(&format!(
"[[rules]]\nwhen.request.url_path = {{ value = \"/v1/orders/{}\", op = \"starts_with\" }}\nrespond = {{ text = \"order-{}\" }}\n\n",
i, i,
)),
_ => toml.push_str(&format!(
"[[rules]]\nwhen.request.url_path = {{ value = \"search-{}\", op = \"contains\" }}\nrespond = {{ text = \"hit-{}\" }}\n\n",
i, i,
)),
}
}
toml.push_str(
"[[rules]]\nwhen.request.url_path = { value = \"*\", op = \"wild_card\" }\nrespond = { status = 404 }\n",
);
std::fs::write(&rule_set_path, toml).expect("write rule set");
let rule_set = RuleSet::new(
rule_set_path.to_str().expect("utf-8 path"),
dir.path().to_str().expect("utf-8 path"),
0,
)
.expect("build rule set");
(dir, rule_set)
}
fn parsed_request_for(url_path: &str) -> ParsedRequest {
let req = Request::builder()
.method("GET")
.uri(url_path)
.body(())
.expect("build request");
let (component_parts, _) = req.into_parts();
ParsedRequest {
url_path: url_path.to_owned(),
component_parts,
body_json: None,
}
}
fn bench_find_matched(c: &mut Criterion) {
let mut group = c.benchmark_group("find_matched");
group.throughput(Throughput::Elements(1));
for &rule_count in &[1usize, 10, 100] {
let (_guard, rule_set) = build_rule_set(rule_count);
let first_hit = parsed_request_for("/api/v1/users/0");
group.bench_with_input(
BenchmarkId::new("first_rule_hit", rule_count),
&rule_count,
|b, _| {
b.iter(|| {
let m = rule_set.find_matched(black_box(&first_hit), None, 0);
black_box(m)
});
},
);
let last_idx = rule_count.saturating_sub(1);
let last_path = match last_idx % 3 {
0 => format!("/api/v1/users/{}", last_idx),
1 => format!("/api/v1/orders/{}/detail", last_idx),
_ => format!("/api/v1/search-{}-results", last_idx),
};
let last_hit = parsed_request_for(&last_path);
group.bench_with_input(
BenchmarkId::new("last_rule_hit", rule_count),
&rule_count,
|b, _| {
b.iter(|| {
let m = rule_set.find_matched(black_box(&last_hit), None, 0);
black_box(m)
});
},
);
let no_match = parsed_request_for("/api/totally/unknown/path");
group.bench_with_input(
BenchmarkId::new("miss_all_specific_rules", rule_count),
&rule_count,
|b, _| {
b.iter(|| {
let m = rule_set.find_matched(black_box(&no_match), None, 0);
black_box(m)
});
},
);
}
group.finish();
}
criterion_group!(benches, bench_find_matched);
criterion_main!(benches);