goatd 0.1.1

Greatest Of All Tree Decompositions: tree decompositions of graphs — elimination orders, FlowCutter, multilevel bisection — with PACE .gr/.td I/O and a command-line solver.
Documentation
use std::time::{Duration, Instant};

use goatd::meter::{
    Guard, UNITS_PER_MS, arm, charge, is_armed, milliseconds_for_units, now, units_spent,
};

#[test]
fn an_unarmed_meter_records_nothing() {
    assert!(!is_armed());
    let before = units_spent();
    charge(1_000_000);
    assert_eq!(units_spent(), before);
}

#[test]
fn the_clock_advances_by_charged_work() {
    let epoch = Instant::now();
    let _guard: Guard = arm(epoch);
    assert_eq!(now(), epoch);

    charge(UNITS_PER_MS * 40);
    assert_eq!(now(), epoch + Duration::from_millis(40));
    charge(UNITS_PER_MS * 2);
    assert_eq!(now(), epoch + Duration::from_millis(42));
}

#[test]
fn identical_charges_reach_the_same_reading() {
    let charges = [3_u64, 100_000, 7, UNITS_PER_MS, 1];
    let readings: Vec<Duration> = (0..2)
        .map(|_| {
            let epoch = Instant::now();
            let _guard = arm(epoch);
            for charge_count in charges {
                charge(charge_count * UNITS_PER_MS);
            }
            now() - epoch
        })
        .collect();
    assert_eq!(readings[0], readings[1]);
}

#[test]
fn the_guard_disarms_when_dropped() {
    {
        let _guard = arm(Instant::now());
        assert!(is_armed());
    }
    assert!(!is_armed());
}

#[test]
fn a_nested_guard_restores_the_outer_clock() {
    let outer_epoch = Instant::now();
    let _outer = arm(outer_epoch);
    charge(UNITS_PER_MS * 3);

    {
        let inner_epoch = outer_epoch + Duration::from_secs(10);
        let _inner = arm(inner_epoch);
        charge(UNITS_PER_MS * 2);
        assert_eq!(now(), inner_epoch + Duration::from_millis(2));
    }

    assert_eq!(now(), outer_epoch + Duration::from_millis(5));
}

#[test]
fn units_convert_to_milliseconds() {
    for milliseconds in [0_u64, 1, 90_000, 3_600_000] {
        assert_eq!(
            milliseconds_for_units(milliseconds.saturating_mul(UNITS_PER_MS)),
            milliseconds
        );
    }
}

#[test]
fn an_absurd_charge_does_not_overflow_the_clock() {
    let epoch = Instant::now();
    let _guard = arm(epoch);
    charge(u64::MAX);
    let reading = now();

    assert!(reading >= epoch);
    if let Some(one_year_later) = epoch.checked_add(Duration::from_secs(365 * 24 * 60 * 60)) {
        assert!(reading >= one_year_later);
    }
}