pipecircuit 0.0.17

Correct. Robust. Fast. Portable. Pick four.
Documentation
use core::num::NonZeroU64;

use super::{
    ElapsedTimerTokens, EmptyElapsedTimerTokens, Timer, TimerIn, TimerOut, TimerToken, WakeAfter,
};
use crate::Pipe;

const fn nonzero(value: u64) -> Result<NonZeroU64, &'static str> {
    match NonZeroU64::new(value) {
        Some(value) => Ok(value),
        None => Err("zero was not positive"),
    }
}

#[test]
fn wake_after_preserves_the_declared_precision() -> Result<(), &'static str> {
    let seconds = WakeAfter::seconds(nonzero(5)?);
    let milliseconds = WakeAfter::milliseconds(nonzero(5_000)?);

    assert_eq!(seconds.checked_nanoseconds(), nonzero(5_000_000_000).ok());
    assert_eq!(
        milliseconds.checked_nanoseconds(),
        seconds.checked_nanoseconds()
    );
    assert_ne!(seconds, milliseconds);
    assert_eq!(seconds.ticks(), nonzero(5)?);
    assert_eq!(
        seconds.precision().nanoseconds_per_tick(),
        nonzero(1_000_000_000)?
    );
    Ok(())
}

#[test]
fn wake_after_reports_nanosecond_overflow() -> Result<(), &'static str> {
    let after = WakeAfter::seconds(nonzero(u64::MAX)?);

    assert_eq!(after.checked_nanoseconds(), None);
    Ok(())
}

#[test]
fn elapsed_batches_are_nonempty() {
    assert_eq!(
        ElapsedTimerTokens::<u8>::try_new(&[]),
        Err(EmptyElapsedTimerTokens)
    );
    let tokens = [3, 5, 8];
    let Ok(elapsed) = ElapsedTimerTokens::try_new(&tokens) else {
        return;
    };
    assert_eq!(elapsed.tokens(), &tokens);
}

#[test]
fn timer_protocol_preserves_arm_cancel_and_elapsed_facts() -> Result<(), &'static str> {
    fn accepts_timer_input(_input: <Timer<u64> as Pipe>::In<'_>) {}
    fn accepts_timer_output(_output: <Timer<u64> as Pipe>::Out<'_>) {}

    let after = WakeAfter::milliseconds(nonzero(25)?);
    accepts_timer_input(TimerIn::Arm { token: 7, after });
    accepts_timer_input(TimerIn::Cancel { token: 7 });

    let tokens = [7, 9];
    let elapsed = ElapsedTimerTokens::try_new(&tokens).map_err(|_| "batch was empty")?;
    accepts_timer_output(TimerOut::Elapsed { tokens: elapsed });
    Ok(())
}

#[test]
fn timer_tokens_preserve_their_nonzero_value() -> Result<(), &'static str> {
    let timer_token = TimerToken::from_nonzero(nonzero(17)?);

    assert_eq!(timer_token.value(), nonzero(17)?);
    Ok(())
}