git_perf/
audit.rs

1use crate::{
2    data::{MeasurementData, ReductionFunc},
3    measurement_retrieval::{self, summarize_measurements},
4    stats,
5};
6use anyhow::{anyhow, bail, Result};
7use itertools::Itertools;
8use log::error;
9use std::iter;
10
11pub fn audit(
12    measurement: &str,
13    max_count: usize,
14    min_count: u16,
15    selectors: &[(String, String)],
16    summarize_by: ReductionFunc,
17    sigma: f64,
18) -> Result<()> {
19    let all = measurement_retrieval::walk_commits(max_count)?;
20
21    let filter_by = |m: &MeasurementData| {
22        m.name == measurement
23            && selectors
24                .iter()
25                .all(|s| m.key_values.get(&s.0).map(|v| *v == s.1).unwrap_or(false))
26    };
27
28    let mut aggregates = summarize_measurements(all, &summarize_by, &filter_by);
29
30    let head = aggregates
31        .next()
32        .ok_or(anyhow!("No commit at HEAD"))
33        .and_then(|s| {
34            s.and_then(|cs| {
35                cs.measurement
36                    .map(|m| m.val)
37                    .ok_or(anyhow!("No measurement for HEAD."))
38            })
39        })?;
40
41    let tail: Vec<_> = aggregates
42        .filter_map_ok(|cs| cs.measurement.map(|m| m.val))
43        .take(max_count)
44        .try_collect()?;
45
46    let head_summary = stats::aggregate_measurements(iter::once(head));
47    let tail_summary = stats::aggregate_measurements(tail.into_iter());
48
49    if tail_summary.len < min_count.into() {
50        // TODO(kaihowl) handle with explicit return? Print text somewhere else?
51        let number_measurements = tail_summary.len;
52        let plural_s = if number_measurements > 1 { "s" } else { "" };
53        error!("Only {number_measurements} measurement{plural_s} found. Less than requested min_measurements of {min_count}. Skipping test.");
54        return Ok(());
55    }
56
57    if head_summary.significantly_different_from(&tail_summary, sigma) {
58        // TODO(kaihowl) print details
59        bail!(
60            "HEAD differs significantly from tail measurements.\nHead: {}\nTail: {}",
61            &head_summary,
62            &tail_summary
63        );
64    }
65
66    Ok(())
67}