subms 0.2.1

Zero-dependency perf-test harness for Rust: timed stages, percentiles, and a stable JSON shape consumed by the submillisecond.com cookbook. Includes a `Recipe` trait for cookbook benchmarks.
Documentation
use subms::{benchmark, BenchParams, Lcg, PerfHarness, Recipe};

struct NoopRecipe;

impl Recipe for NoopRecipe {
    fn name(&self) -> &str {
        "noop"
    }

    fn run(&self, h: &mut PerfHarness, params: &BenchParams) {
        let stage = h.stage("noop", params.entries);
        for _ in 0..params.entries {
            stage.time(|| {});
        }
    }
}

#[test]
fn benchmark_produces_expected_json_shape() {
    let params = BenchParams {
        entries: 50,
        warmup: 0,
        seed: 0,
    };
    let h = benchmark(&NoopRecipe, &params);

    let mut buf = Vec::new();
    h.write_json(&mut buf).unwrap();
    let json = String::from_utf8(buf).unwrap();

    assert!(json.contains("\"workload\":\"noop\""));
    assert!(json.contains("\"lang\":\"rust\""));
    assert!(json.contains("\"entries\":\"50\""));
    assert!(json.contains("\"warmup\":\"0\""));
    assert!(json.contains("\"seed\":\"0\""));
    assert!(json.contains("\"noop\":{"));
    assert!(json.contains("\"count\":50"));
}

#[test]
fn bench_params_default_is_sensible() {
    let d = BenchParams::default();
    assert!(d.entries > 0);
    assert!(d.warmup <= d.entries);
}

#[test]
fn lcg_reexport_works() {
    let mut rng = Lcg::new(123);
    let _ = rng.bounded(10);
}