ellip_dev_utils/
benchmark.rs

1/*
2 * Ellip is licensed under The 3-Clause BSD, see LICENSE.
3 * Copyright 2025 Sira Pornsiriprasert <code@psira.me>
4 */
5
6use std::fs;
7use std::path::Path;
8
9use crate::StrErr;
10
11/// Criterion benchmark estimates structure
12#[derive(Debug, serde::Deserialize)]
13struct CriterionEstimates {
14    mean: CriterionMean,
15}
16
17/// Criterion mean structure
18#[derive(Debug, serde::Deserialize)]
19struct CriterionMean {
20    point_estimate: f64,
21}
22
23pub fn extract_criterion_mean(benchmark_path: &str) -> Result<f64, StrErr> {
24    let estimates_path = Path::new(benchmark_path).join("estimates.json");
25
26    let content =
27        fs::read_to_string(&estimates_path).map_err(|_| "Cannot read estimates.json file")?;
28
29    let estimates: CriterionEstimates =
30        serde_json::from_str(&content).map_err(|_| "Cannot parse estimates.json file")?;
31
32    Ok(estimates.mean.point_estimate)
33}
34
35pub fn extract_criterion_means(benchmark_paths: &[&str]) -> Result<Vec<f64>, StrErr> {
36    benchmark_paths
37        .iter()
38        .map(|path| extract_criterion_mean(path))
39        .collect()
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use std::fs;
46    use tempfile::tempdir;
47
48    #[test]
49    fn test_extract_criterion_mean() {
50        // Create a temporary directory with a mock estimates.json
51        let temp_dir = tempdir().unwrap();
52        let estimates_content = r#"{
53            "mean": {
54                "confidence_interval": {
55                    "confidence_level": 0.95,
56                    "lower_bound": 100.0,
57                    "upper_bound": 200.0
58                },
59                "point_estimate": 150.5,
60                "standard_error": 25.0
61            }
62        }"#;
63
64        let estimates_path = temp_dir.path().join("estimates.json");
65        fs::write(&estimates_path, estimates_content).unwrap();
66
67        let mean = extract_criterion_mean(temp_dir.path().to_str().unwrap()).unwrap();
68        assert_eq!(mean, 150.5);
69    }
70
71    #[test]
72    fn test_extract_criterion_means() {
73        // Create temporary directories with mock estimates.json files
74        let temp_dir1 = tempdir().unwrap();
75        let temp_dir2 = tempdir().unwrap();
76
77        let estimates_content1 = r#"{"mean":{"point_estimate":100.0}}"#;
78        let estimates_content2 = r#"{"mean":{"point_estimate":200.0}}"#;
79
80        fs::write(temp_dir1.path().join("estimates.json"), estimates_content1).unwrap();
81        fs::write(temp_dir2.path().join("estimates.json"), estimates_content2).unwrap();
82
83        let paths = vec![
84            temp_dir1.path().to_str().unwrap(),
85            temp_dir2.path().to_str().unwrap(),
86        ];
87
88        let means = extract_criterion_means(&paths).unwrap();
89        assert_eq!(means, vec![100.0, 200.0]);
90    }
91}