Skip to content

Commit 8fe9009

Browse files
hyperpolymathclaude
andcommitted
test: add criterion benchmarks for intsoc-core domain model
Benchmarks cover draft name parsing, IETF state machine transitions (single and full lifecycle), organization metadata lookups, and CheckSummary construction at varying result counts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3f93793 commit 8fe9009

3 files changed

Lines changed: 232 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ similar = { version = "2", features = ["text"] }
5757
# Git
5858
gix = { version = "0.79", default-features = false, features = ["basic", "blocking-network-client"] }
5959

60+
# Benchmarking
61+
criterion = { version = "0.5", features = ["html_reports"] }
62+
6063
# Workspace crates
6164
intsoc-core = { path = "crates/intsoc-core" }
6265
intsoc-parser = { path = "crates/intsoc-parser" }

crates/intsoc-core/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,12 @@ chrono = { workspace = true }
1717
thiserror = { workspace = true }
1818
tracing = { workspace = true }
1919

20+
[dev-dependencies]
21+
criterion = { workspace = true }
22+
23+
[[bench]]
24+
name = "intsoc_core_bench"
25+
harness = false
26+
2027
[lints]
2128
workspace = true
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell
3+
4+
//! intsoc-core benchmarks — domain model construction, state machine transitions,
5+
//! and draft name parsing.
6+
//!
7+
//! Covers the hot paths used during batch document processing:
8+
//! - `InternetDraft::parse_draft_name` — called on every document ingest
9+
//! - `IetfState::valid_transitions` — called on every state machine step
10+
//! - `StateMachine::transition` — full state advance including history append
11+
//! - `Organization::datatracker_base` + `uses_datatracker` — called per request
12+
13+
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
14+
use intsoc_core::{
15+
document::InternetDraft,
16+
organization::Organization,
17+
state::{IetfState, StateMachine, StreamState},
18+
stream::Stream,
19+
validation::{CheckCategory, CheckResult, CheckSummary, Fixability, Severity},
20+
};
21+
22+
// ============================================================================
23+
// Draft name parsing benchmarks
24+
// ============================================================================
25+
26+
/// Benchmark parsing a well-formed draft name (common fast path).
27+
fn bench_parse_draft_name_valid(c: &mut Criterion) {
28+
let draft_names = [
29+
"draft-ietf-quic-transport-34",
30+
"draft-ietf-tls-rfc8446bis-09",
31+
"draft-iab-protocol-maintenance-14",
32+
"draft-irtf-cfrg-hpke-12",
33+
"draft-ietf-httpbis-semantics-19",
34+
];
35+
36+
c.bench_function("parse_draft_name_valid", |b| {
37+
b.iter(|| {
38+
for name in &draft_names {
39+
black_box(InternetDraft::parse_draft_name(black_box(name)));
40+
}
41+
})
42+
});
43+
}
44+
45+
/// Benchmark parsing an invalid draft name (error path).
46+
fn bench_parse_draft_name_invalid(c: &mut Criterion) {
47+
let invalid_names = [
48+
"not-a-draft",
49+
"draft-no-version",
50+
"draft-",
51+
"",
52+
"RFC7230",
53+
];
54+
55+
c.bench_function("parse_draft_name_invalid", |b| {
56+
b.iter(|| {
57+
for name in &invalid_names {
58+
black_box(InternetDraft::parse_draft_name(black_box(name)));
59+
}
60+
})
61+
});
62+
}
63+
64+
// ============================================================================
65+
// State machine benchmarks
66+
// ============================================================================
67+
68+
/// Benchmark querying valid transitions from each IETF state.
69+
///
70+
/// This is called on every UI render and state display, so it needs
71+
/// to be cheap even for complex state graphs.
72+
fn bench_ietf_valid_transitions(c: &mut Criterion) {
73+
let states = [
74+
IetfState::Draft,
75+
IetfState::IdnitsCheck,
76+
IetfState::WgDocument,
77+
IetfState::IesgEvaluation,
78+
IetfState::Approved,
79+
];
80+
81+
c.bench_function("ietf_valid_transitions", |b| {
82+
b.iter(|| {
83+
for state in &states {
84+
black_box(state.valid_transitions());
85+
}
86+
})
87+
});
88+
}
89+
90+
/// Benchmark creating a new StateMachine starting from the initial IETF state.
91+
fn bench_state_machine_new(c: &mut Criterion) {
92+
c.bench_function("state_machine_new_ietf", |b| {
93+
b.iter(|| black_box(StateMachine::<IetfState>::new()))
94+
});
95+
}
96+
97+
/// Benchmark a single state transition (Draft -> IdnitsCheck).
98+
fn bench_state_machine_single_transition(c: &mut Criterion) {
99+
c.bench_function("state_machine_transition_single", |b| {
100+
b.iter(|| {
101+
let mut sm = StateMachine::<IetfState>::new();
102+
black_box(sm.transition(black_box(IetfState::IdnitsCheck)))
103+
})
104+
});
105+
}
106+
107+
/// Benchmark walking the full happy-path IETF lifecycle:
108+
/// Draft -> IdnitsCheck -> WgDocument -> ... -> Published
109+
fn bench_state_machine_full_lifecycle(c: &mut Criterion) {
110+
// Represents the happy-path transitions for a WG document.
111+
let lifecycle = [
112+
IetfState::IdnitsCheck,
113+
IetfState::IndividualSubmitted,
114+
IetfState::WgAdopted,
115+
IetfState::WgDocument,
116+
IetfState::WgLastCall,
117+
IetfState::WaitingForWriteup,
118+
IetfState::AdEvaluation,
119+
IetfState::IesgEvaluation,
120+
IetfState::IesgLastCall,
121+
IetfState::Approved,
122+
IetfState::RfcEditorQueue,
123+
IetfState::Auth48,
124+
IetfState::Published,
125+
];
126+
127+
c.bench_function("state_machine_full_lifecycle", |b| {
128+
b.iter(|| {
129+
let mut sm = StateMachine::<IetfState>::new();
130+
for next_state in &lifecycle {
131+
let _ = sm.transition(black_box(next_state.clone()));
132+
}
133+
black_box(sm.is_complete())
134+
})
135+
});
136+
}
137+
138+
/// Benchmark checking `is_terminal` for all IETF states.
139+
fn bench_is_terminal_all_states(c: &mut Criterion) {
140+
let states = [
141+
IetfState::Draft,
142+
IetfState::Published,
143+
IetfState::Withdrawn,
144+
IetfState::Expired,
145+
IetfState::WgDocument,
146+
];
147+
148+
c.bench_function("is_terminal_all_states", |b| {
149+
b.iter(|| {
150+
for s in &states {
151+
black_box(s.is_terminal());
152+
}
153+
})
154+
});
155+
}
156+
157+
// ============================================================================
158+
// Organization benchmarks
159+
// ============================================================================
160+
161+
/// Benchmark organization metadata lookups (called per HTTP request).
162+
fn bench_organization_metadata(c: &mut Criterion) {
163+
let orgs = [
164+
Organization::Ietf,
165+
Organization::Irtf,
166+
Organization::Iab,
167+
Organization::Independent,
168+
Organization::Iana,
169+
Organization::RfcEditor,
170+
];
171+
172+
c.bench_function("organization_datatracker_base", |b| {
173+
b.iter(|| {
174+
for org in &orgs {
175+
black_box(org.datatracker_base());
176+
black_box(org.uses_datatracker());
177+
}
178+
})
179+
});
180+
}
181+
182+
// ============================================================================
183+
// ValidationReport benchmarks
184+
// ============================================================================
185+
186+
/// Benchmark building a ValidationReport from N check results.
187+
fn bench_validation_report_build(c: &mut Criterion) {
188+
let make_result = |i: u32, severity: Severity| CheckResult {
189+
check_id: format!("check-{i:04}"),
190+
severity,
191+
message: format!("Check {i} finding"),
192+
location: None,
193+
category: CheckCategory::Boilerplate,
194+
fixable: Fixability::AutoSafe,
195+
suggestion: None,
196+
};
197+
198+
let mut group = c.benchmark_group("validation_report_n");
199+
for n in [8usize, 32, 128, 512] {
200+
let results: Vec<CheckResult> = (0..n as u32)
201+
.map(|i| make_result(i, if i % 3 == 0 { Severity::Error } else { Severity::Warning }))
202+
.collect();
203+
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, _| {
204+
b.iter(|| black_box(CheckSummary::from_results(black_box(results.clone()))))
205+
});
206+
}
207+
group.finish();
208+
}
209+
210+
criterion_group!(
211+
benches,
212+
bench_parse_draft_name_valid,
213+
bench_parse_draft_name_invalid,
214+
bench_ietf_valid_transitions,
215+
bench_state_machine_new,
216+
bench_state_machine_single_transition,
217+
bench_state_machine_full_lifecycle,
218+
bench_is_terminal_all_states,
219+
bench_organization_metadata,
220+
bench_validation_report_build,
221+
);
222+
criterion_main!(benches);

0 commit comments

Comments
 (0)