hail_core 0.3.0

a library for implementing a speedrun timer
Documentation
mod common;
use common::*;
use hail_core::types::*;
use std::time::Instant;

#[test]
#[ignore]
fn basic_state_transitions() {
    let mut s = init();
    assert_eq!(s.changes, vec![StateChange::Reset]);
    let now = Instant::now();
    s.request(StateChangeRequest::Split); // start the run
    s.update(); // make start take effect
    assert_eq!(s.changes, vec![StateChange::StartSeg { segment: None }]);
    loop {
        sleep(10); // wait through offset
        s.update();
        if !s.changes.is_empty() {
            assert_eq!(s.changes, vec![StateChange::StartSeg { segment: Some(0) }]);
            break;
        }
        if now.elapsed().as_millis() >= 1300 {
            panic!("Offset was not left on time!");
        }
    }
    assert!(!s.in_offset());
    same_time!(s.rta.active_time, 0); // starting a run should be at 0 time
    assert!(now.elapsed().as_millis() >= s.run.offset.val().unsigned_abs()); // but the length of the offset should have passed

    sleep(100);
    s.update();
    same_time!(s.rta.active_time, 100); // t = 100
    assert_eq!(s.rta.active_pb_status, SplitStatus::Ahead); // and we're now ahead

    sleep(400); // t = 500
    s.request(StateChangeRequest::Split); // finish the first split @ t=500ms
    s.update();
    assert!(s.is_rta_running()); // should still be running
    same_time!(s.rta.active_time, 500);
    assert_eq!(
        s.changes,
        vec![
            StateChange::FinishSeg { segment: 0 },
            StateChange::ChangeSeg { old: 0, new: 1 },
            StateChange::StartSeg { segment: Some(1) }
        ]
    );
    same_time!(s.run.avg_segments(TimingMethod::Rta)[0].val(), 766); // updated sum from (2, 1800) to (3, 2300)
    same_time!(s.rta.run_pb_diffs.last().unwrap().val(), -500); // previous split time was 1000ms
    same_time!(s.rta.run_pb_seg_diffs.last().unwrap().val(), -500);
    same_time!(s.rta.run_gold_diffs.last().unwrap().val(), -300); // previous gold split time was 800ms
    same_time!(s.rta.run_gold_seg_diffs.last().unwrap().val(), -300);
    same_time!(s.rta.run_avg_diffs.last().unwrap().val(), -400); // previous average split time was 900ms
    same_time!(s.rta.run_avg_seg_diffs.last().unwrap().val(), -400);
    assert_eq!(s.rta.run_pb_statuses[0], SplitStatus::Gold); // golded this segment
    assert_eq!(s.rta.active_pb_status, SplitStatus::Ahead); // we remain ahead

    s.request(StateChangeRequest::SetComparison(Comparison::Golds));
    s.update();
    assert_eq!(
        s.changes,
        vec![StateChange::ChangeComparison {
            old: Comparison::PersonalBest,
            new: Comparison::Golds
        }]
    );
    s.request(StateChangeRequest::SetComparison(Comparison::PersonalBest));
    s.update();

    sleep(1400); // t = 1900
    s.request(StateChangeRequest::Split); // complete second split
    s.update();
    same_time!(s.rta.active_time, 1900);
    assert_eq!(
        s.changes,
        vec![
            StateChange::FinishSeg { segment: 1 },
            StateChange::ChangeSeg { old: 1, new: 2 },
            StateChange::StartSeg { segment: Some(2) }
        ]
    );
    same_time!(s.run.avg_segments(TimingMethod::Rta)[1].val(), 1200); // (1, 1000) -> (2, 2400)
    same_time!(s.rta.run_pb_diffs.last().unwrap().val(), -100); // split time of 2nd pb split was 2000
    same_time!(s.rta.run_pb_seg_diffs.last().unwrap().val(), 400);
    same_time!(s.rta.run_gold_diffs.last().unwrap().val(), 1000); // split time of 2nd gold split was 900
    same_time!(s.rta.run_gold_seg_diffs.last().unwrap().val(), 1300);
    same_time!(s.rta.run_avg_diffs.last().unwrap().val(), 0); // split time of 2nd avg split was 1900
    same_time!(s.rta.run_avg_seg_diffs.last().unwrap().val(), 400);
    assert_eq!(s.rta.active_pb_status, SplitStatus::Ahead); // we remain ahead
    sleep(1150); // t = 3050
    s.request(StateChangeRequest::Split); // finish remaining splits
    s.update();
    assert_eq!(*s.rta.run_pb_statuses.last().unwrap(), SplitStatus::Behind);
    sleep(50); // t = 3100
    s.request(StateChangeRequest::Split);
    s.update();
    assert_eq!(s.changes, vec![StateChange::FinishSeg { segment: 3 }]);
    same_time!(s.run.rta_pb_splits[0].val(), 500); // times have been written to PB since we PB'd
    same_time!(s.run.rta_pb_splits[1].val(), 1900);
    // gold should have been forced
    same_time!(s.run.rta_gold_segments[3].val(), 50);
    assert_eq!(s.rta.active_pb_status, SplitStatus::Ahead);
}

#[test]
#[ignore]
fn unsplit() {
    let mut s = init();
    s.request(StateChangeRequest::Split);
    s.update();
    sleep(2100); // 1200ms of offset + t = 900, this is 100ms ahead of pb
    s.request(StateChangeRequest::Split);
    s.update();
    same_time!(s.rta.run_pb_diffs.last().unwrap().val(), -100);
    sleep(50); // t = 950, 1050ms ahead of 2nd pb split (2000ms)
    s.request(StateChangeRequest::Split);
    s.update();
    // force a gold
    assert_eq!(s.rta.run_pb_statuses[1], SplitStatus::Gold);
    same_time!(s.rta.run_pb_diffs.last().unwrap().val(), -1050);
    sleep(1400); // t = 2350, 650ms ahead
    s.request(StateChangeRequest::Split);
    s.update();
    same_time!(s.rta.run_pb_diffs.last().unwrap().val(), -650);
    s.request(StateChangeRequest::Unsplit);
    s.update();
    same_time!(s.rta.run_pb_diffs.last().unwrap().val(), -1050); // last one has been popped
    assert_eq!(s.rta.run_split_times.len(), 2);
    assert_eq!(s.rta.run_pb_diffs.len(), 2);
    assert_eq!(s.rta.run_gold_diffs.len(), 2);
    assert_eq!(s.rta.run_avg_diffs.len(), 2);
    assert_eq!(s.rta.run_pb_statuses.len(), 2);
    assert_eq!(s.current_seg, 2);
    assert_eq!(s.run.avg_segments(TimingMethod::Rta)[2], TimeType::None); // removed average data
    s.request(StateChangeRequest::Unsplit);
    s.update();
    same_time!(s.rta.run_pb_diffs.last().unwrap().val(), -100);
    assert_eq!(s.rta.run_split_times.len(), 1);
    assert_eq!(s.rta.run_pb_diffs.len(), 1);
    assert_eq!(s.rta.run_gold_diffs.len(), 1);
    assert_eq!(s.rta.run_avg_diffs.len(), 1);
    assert_eq!(s.rta.run_pb_statuses.len(), 1);
    assert_eq!(s.current_seg, 1);
    assert_eq!(s.run.avg_segments(TimingMethod::Rta)[1].val(), 1000);
    same_time!(s.run.rta_gold_segments[1].val(), 100); // ensure gold was not written
}

#[test]
#[ignore]
fn empty_run() {
    let mut s = init_empty();
    s.request(StateChangeRequest::Split);
    s.update();
    sleep(1000); // t = 1000; 1000ms into first split
    s.request(StateChangeRequest::Split);
    s.update();
    same_time!(s.rta.run_split_times[0].val(), 1000);
    assert_eq!(s.rta.active_pb_status, SplitStatus::None);
    s.request(StateChangeRequest::Reset);
    s.update();
    same_time!(s.run.rta_gold_segments[0].val(), 1000);

    s.request(StateChangeRequest::Split);
    s.update();
    sleep(1100);
    s.request(StateChangeRequest::Split);
    s.update();
    same_time!(s.run.avg_segments(TimingMethod::Rta)[0].val(), 1050);
}