//! Unit tests of the display match (capability-constraints §4.2) and of a sink's writer.

use super::*;

fn ladder() -> Vec<Rendition> {
    vec![
        Rendition::new(640, 360, 30_000, 8, TRANSFER_SDR),
        Rendition::new(1280, 720, 30_000, 8, TRANSFER_SDR),
        Rendition::new(1920, 1080, 30_000, 8, TRANSFER_SDR),
        Rendition::new(3840, 2160, 60_000, 10, TRANSFER_PQ),
    ]
}

#[test]
fn sides_have_no_orientation() {
    let r = Rendition::new(1920, 1080, 30_000, 8, TRANSFER_SDR);
    assert_eq!(exceedances(&r, &Declaration::panel(1080, 1920, 60_000, 8, false)), 0);
    assert_eq!(exceedances(&Rendition::new(1080, 1920, 30_000, 8, TRANSFER_SDR), &Declaration::panel(1920, 1080, 60_000, 8, false)), 0);
    assert_eq!(exceedances(&r, &Declaration::panel(1280, 720, 60_000, 8, false)), exceed::RESOLUTION);
}

#[test]
fn each_exceedance_alone() {
    let d = Declaration::panel(3840, 2160, 60_000, 8, false);
    assert_eq!(exceedances(&Rendition::new(3840, 2160, 60_001, 8, TRANSFER_SDR), &d), exceed::REFRESH);
    assert_eq!(exceedances(&Rendition::new(3840, 2160, 60_000, 9, TRANSFER_SDR), &d), exceed::BIT_DEPTH);
    assert_eq!(exceedances(&Rendition::new(3840, 2160, 60_000, 8, TRANSFER_HLG), &d), exceed::DYNAMIC_RANGE);
    assert_eq!(exceedances(&Rendition::new(3840, 2160, 60_000, 8, TRANSFER_PQ), &Declaration { hdr: true, ..d }), 0);
    // A transfer that is neither PQ nor HLG is not above any panel.
    assert_eq!(exceedances(&Rendition::new(3840, 2160, 60_000, 8, 14), &d), 0);
}

#[test]
fn the_least_alone_where_none_is_within() {
    let small = Declaration::panel(320, 240, 60_000, 8, false);
    let l = ladder();
    assert_eq!(admissible(&l, &small), vec![0]);
    assert_eq!(match_of(&l, 0, &small), exceed::RESOLUTION | exceed::NONE_WITHIN);
    // 60 pictures a second on a 60 Hz panel is not above it.
    assert_eq!(match_of(&l, 3, &small), exceed::RESOLUTION | exceed::BIT_DEPTH | exceed::DYNAMIC_RANGE | exceed::NONE_WITHIN);
    let slow = Declaration { refresh_milli: 59_999, ..small };
    assert_eq!(match_of(&l, 3, &slow), exceed::ALL);
    let tv = Declaration::panel(1920, 1080, 60_000, 8, false);
    assert_eq!(admissible(&l, &tv), vec![0, 1, 2]);
    assert_eq!(choose(&l, &tv), Some(2));
    assert_eq!(choose(&l, &small), Some(0));
    assert_eq!(least(&[]), None);
}

#[test]
fn a_writer_refuses_after_its_termination_and_its_log_verifies() {
    let key = SigningKey::from_bytes(&[7; 32]);
    let open = Open {
        stream: [1; 32],
        media: media::VIDEO,
        start: start::USER,
        autoplay: setting::ON,
        ua_policy: ua_policy::NOT_ASKED,
        unseen_grace_ms: 1_000,
        idle_ms: 0,
        autoplay_grace_ms: 0,
        display: Declaration::panel(1920, 1080, 60_000, 8, false),
        ladder: ladder(),
    };
    let mut w = Writer::open(key, 1_000_000, open);
    let seg = |k: u64| Segment { segment: [k as u8; 32], objects: 30, bytes: 1000, media_start: k * 1_000_000, media_end: (k + 1) * 1_000_000, rendition: 2, matched: 0, hops: vec![HopEntry::unmetered(role::SINK_DECODE)] };
    w.segment(2_000_000, seg(0)).unwrap();
    w.observe(2_500_000, signal::VISIBILITY, signal::UNSEEN).unwrap();
    w.terminate(3_500_000, why::HIDDEN, 1_000_000).unwrap();
    assert_eq!(w.segment(4_000_000, seg(1)).unwrap_err(), Reason::AfterTermination);
    let a = verify_log(w.records()).unwrap();
    assert_eq!(a.terminated, Some((why::HIDDEN, 1_000_000)));
    assert_eq!(a.unseen_us, Some(1_000_000));
    assert_eq!(a.absent, vec![role::ORIGIN, role::RELAY, role::DISPLAY]);
}

#[test]
fn reasons_are_listed_once() {
    let mut codes: Vec<&str> = Reason::ALL.iter().map(|r| r.code()).collect();
    codes.sort();
    codes.dedup();
    assert_eq!(codes.len(), Reason::ALL.len());
    assert!(Reason::OWN.iter().all(|r| Reason::ALL.contains(r)));
}
