From 20826ae975dbdd77324ec36711ecac4fad7157ab Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 21 Mar 2026 15:49:31 -0400 Subject: [PATCH] chore(bench/html): add real test fixtures to biome_html_analyze benches --- .../biome_html_analyze/benches/fixtures/real | 1 + .../benches/html_analyzer.rs | 107 ++++++++++++++++-- 2 files changed, 98 insertions(+), 10 deletions(-) create mode 120000 crates/biome_html_analyze/benches/fixtures/real diff --git a/crates/biome_html_analyze/benches/fixtures/real b/crates/biome_html_analyze/benches/fixtures/real new file mode 120000 index 000000000000..555b3306a001 --- /dev/null +++ b/crates/biome_html_analyze/benches/fixtures/real @@ -0,0 +1 @@ +../../../biome_html_parser/benches/fixtures/real \ No newline at end of file diff --git a/crates/biome_html_analyze/benches/html_analyzer.rs b/crates/biome_html_analyze/benches/html_analyzer.rs index 0215a2f6ebbf..06e930e77717 100644 --- a/crates/biome_html_analyze/benches/html_analyzer.rs +++ b/crates/biome_html_analyze/benches/html_analyzer.rs @@ -1,3 +1,5 @@ +use std::{collections::HashMap, fs, path::Path}; + use biome_analyze::options::JsxRuntime; use biome_analyze::{ AnalysisFilter, AnalyzerConfiguration, AnalyzerOptions, ControlFlow, Never, @@ -6,8 +8,7 @@ use biome_analyze::{ use biome_html_parser::{HtmlParserOptions, parse_html}; use biome_html_syntax::HtmlFileSource; use biome_test_utils::BenchCase; -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; -use std::collections::HashMap; +use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main}; #[cfg(target_os = "windows")] #[global_allocator] @@ -24,11 +25,58 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; #[cfg(all(target_env = "musl", target_os = "linux", target_arch = "aarch64"))] #[global_allocator] static GLOBAL: std::alloc::System = std::alloc::System; + +fn load_fixtures() -> Result, std::io::Error> { + let fixtures_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("benches/fixtures"); + let mut cases = Vec::new(); + + fn visit( + dir: &Path, + root: &Path, + cases: &mut Vec<(String, String, String)>, + ) -> Result<(), std::io::Error> { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + visit(&path, root, cases)?; + } else if path.is_file() { + if matches!(path.extension().and_then(|e| e.to_str()), Some(ext) if ext.eq_ignore_ascii_case("md")) + { + continue; + } + + let rel = path.strip_prefix(root).unwrap_or(&path); + let group = rel + .iter() + .next() + .and_then(|segment| segment.to_str()) + .unwrap_or("root") + .to_string(); + let name = path + .file_name() + .and_then(|segment| segment.to_str()) + .unwrap_or_default() + .to_string(); + let content = fs::read_to_string(&path)?; + + cases.push((group, name, content)); + } + } + + Ok(()) + } + + visit(&fixtures_root, &fixtures_root, &mut cases)?; + Ok(cases) +} + fn bench_analyzer(criterion: &mut Criterion) { let mut all_suites = HashMap::new(); all_suites.insert("html", include_str!("libs-html.txt")); let mut libs = vec![]; libs.extend(all_suites.values().flat_map(|suite| suite.lines())); + let fixtures = load_fixtures().expect("failed to load fixtures"); let mut group = criterion.benchmark_group("html_analyzer"); @@ -38,14 +86,16 @@ fn bench_analyzer(criterion: &mut Criterion) { match test_case { Ok(test_case) => { let code = test_case.code(); - let file_source = HtmlFileSource::html(); - group.throughput(criterion::Throughput::Bytes(code.len() as u64)); + let file_source = HtmlFileSource::try_from(test_case.path()).unwrap_or_else(|_| { + panic!("failed to determine file source for {}", test_case.path()) + }); + let parse = parse_html(code, HtmlParserOptions::from(&file_source)); + + group.throughput(Throughput::Bytes(code.len() as u64)); group.bench_with_input( BenchmarkId::from_parameter(test_case.filename()), code, |b, _| { - let parse = parse_html(code, HtmlParserOptions::from(&file_source)); - let filter = AnalysisFilter { categories: RuleCategoriesBuilder::default() .with_syntax() @@ -54,10 +104,8 @@ fn bench_analyzer(criterion: &mut Criterion) { .build(), ..AnalysisFilter::default() }; - let options = AnalyzerOptions::default().with_configuration( - AnalyzerConfiguration::default() - .with_jsx_runtime(JsxRuntime::default()), - ); + let options = AnalyzerOptions::default() + .with_configuration(AnalyzerConfiguration::default()); b.iter(|| { biome_html_analyze::analyze( @@ -79,6 +127,45 @@ fn bench_analyzer(criterion: &mut Criterion) { } } + for (group_name, name, content) in fixtures { + let code = content.as_str(); + let ext = name.rsplit('.').next().unwrap_or_default(); + let file_source = HtmlFileSource::try_from_extension(ext).unwrap_or_else(|_| { + panic!("failed to determine file source for fixture {group_name}/{name}") + }); + let parse = parse_html(code, HtmlParserOptions::from(&file_source)); + let id = format!("{group_name}/{name}"); + + group.throughput(Throughput::Bytes(code.len() as u64)); + group.bench_with_input(BenchmarkId::from_parameter(&id), code, |b, _| { + let filter = AnalysisFilter { + categories: RuleCategoriesBuilder::default() + .with_syntax() + .with_lint() + .with_assist() + .build(), + ..AnalysisFilter::default() + }; + let options = AnalyzerOptions::default().with_configuration( + AnalyzerConfiguration::default().with_jsx_runtime(JsxRuntime::default()), + ); + + b.iter(|| { + biome_html_analyze::analyze( + &parse.tree(), + filter, + &options, + file_source, + |event| { + black_box(event.diagnostic()); + black_box(event.actions()); + ControlFlow::::Continue(()) + }, + ); + }); + }); + } + group.finish(); }