tastty-core 0.1.0

Sans-IO core of the tastty terminal session library: VT parser, screen buffer, and byte encoders.
use super::*;

#[test]
fn xtgettcap_tn() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query TN (hex "544e")
    process(&mut parser, b"\x1bP+q544E\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    // Should respond with DCS 1 + r 544E=<hex("tastty")> ST
    assert!(resp_str.starts_with("\x1bP1+r544E="), "got: {resp_str:?}");
    assert!(resp_str.ends_with("\x1b\\"));
    // Decode the value: "tastty" = 746173747479
    assert!(resp_str.contains("746173747479"), "got: {resp_str:?}");
}

#[test]
fn xtgettcap_rgb() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query RGB (hex "524742")
    process(&mut parser, b"\x1bP+q524742\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    assert!(resp_str.starts_with("\x1bP1+r524742="), "got: {resp_str:?}");
    // Value "8" = hex "38"
    assert!(resp_str.contains("38"), "got: {resp_str:?}");
}

#[test]
fn xtgettcap_tc_boolean() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query Tc (hex "5463")
    process(&mut parser, b"\x1bP+q5463\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    // Boolean: DCS 1 + r 5463= ST (= present, empty value)
    assert_eq!(resp_str, "\x1bP1+r5463=\x1b\\");
}

#[test]
fn xtgettcap_smulx() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query Smulx (hex "536D756C78")
    process(&mut parser, b"\x1bP+q536D756C78\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    assert!(
        resp_str.starts_with("\x1bP1+r536D756C78="),
        "got: {resp_str:?}"
    );
}

#[test]
fn xtgettcap_se_cursor_reset() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query Se (hex "5365")
    process(&mut parser, b"\x1bP+q5365\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    assert!(resp_str.starts_with("\x1bP1+r5365="), "got: {resp_str:?}");
    // Value is ESC[2 q = 1B 5B 32 20 71
    assert!(resp_str.contains("1B5B322071"), "got: {resp_str:?}");
}

#[test]
fn xtgettcap_sitm_italic() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query sitm (hex "7369746D")
    process(&mut parser, b"\x1bP+q7369746D\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    assert!(
        resp_str.starts_with("\x1bP1+r7369746D="),
        "got: {resp_str:?}"
    );
    // Value is ESC[3m = 1B 5B 33 6D
    assert!(resp_str.contains("1B5B336D"), "got: {resp_str:?}");
}

#[test]
fn xtgettcap_ritm_end_italic() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query ritm (hex "7269746D")
    process(&mut parser, b"\x1bP+q7269746D\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    assert!(
        resp_str.starts_with("\x1bP1+r7269746D="),
        "got: {resp_str:?}"
    );
    // Value is ESC[23m = 1B 5B 32 33 6D
    assert!(resp_str.contains("1B5B32336D"), "got: {resp_str:?}");
}

#[test]
fn xtgettcap_unknown_cap() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query unknown cap "ZZ" (hex "5A5A")
    process(&mut parser, b"\x1bP+q5A5A\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    // Should respond with DCS 0 + r (not found)
    assert_eq!(resp_str, "\x1bP0+r5A5A\x1b\\");
}

#[test]
fn xtgettcap_ms_clipboard() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query Ms (hex "4D73") for clipboard access
    process(&mut parser, b"\x1bP+q4D73\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    assert!(resp_str.starts_with("\x1bP1+r4D73="), "got: {resp_str:?}");
}

#[test]
fn xtgettcap_multiple_keys() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query TN and Tc in one request, semicolon-separated
    process(&mut parser, b"\x1bP+q544E;5463\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    // Should get two responses concatenated
    assert!(resp_str.contains("\x1bP1+r544E="), "got: {resp_str:?}");
    assert!(
        resp_str.contains("\x1bP1+r5463=\x1b\\"),
        "got: {resp_str:?}"
    );
}

#[test]
fn xtgettcap_lowercase_hex() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    // Query TN with lowercase hex: "544e" instead of "544E"
    process(&mut parser, b"\x1bP+q544e\x1b\\");
    let resp = drain_replies(&mut parser);
    let resp_str = std::str::from_utf8(&resp).unwrap();
    // Should still find it (we uppercase before lookup)
    assert!(resp_str.starts_with("\x1bP1+r544E="), "got: {resp_str:?}");
}