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 sgr_colors() {
    let mut screen = make_screen(24, 80);
    // \x1b[31m -> red fg
    let mut parser = vte::Parser::new();
    parser.advance(
        &mut crate::perform::PerformScreen::new(&mut screen),
        b"\x1b[31m",
    );
    screen.text('R');
    let cell = screen.cell(0, 0).unwrap();
    assert_eq!(cell.attrs().fg_color, crate::attrs::Color::Index(1));
}

#[test]
fn sgr_strikethrough() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[9mX\x1b[29mY");
    let screen = parser.screen();
    assert!(screen.cell(0, 0).unwrap().attrs().strikethrough());
    assert!(!screen.cell(0, 1).unwrap().attrs().strikethrough());
}

#[test]
fn sgr_hidden() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[8mX\x1b[28mY");
    let screen = parser.screen();
    assert!(screen.cell(0, 0).unwrap().attrs().hidden());
    assert!(!screen.cell(0, 1).unwrap().attrs().hidden());
}

#[test]
fn sgr_reset_clears_strikethrough_and_hidden() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[9;8mX\x1b[0mY");
    let screen = parser.screen();
    let x = screen.cell(0, 0).unwrap();
    assert!(x.attrs().strikethrough());
    assert!(x.attrs().hidden());
    let y = screen.cell(0, 1).unwrap();
    assert!(!y.attrs().strikethrough());
    assert!(!y.attrs().hidden());
}

#[test]
fn sgr_underline_single() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[4mA");
    let cell = parser.screen().cell(0, 0).unwrap();
    assert!(cell.attrs().underline());
    assert_eq!(
        cell.attrs().underline_style(),
        crate::attrs::UnderlineStyle::Single
    );
}

#[test]
fn sgr_underline_variants() {
    let cases = [
        (b"\x1b[4:0mA".as_slice(), crate::attrs::UnderlineStyle::None),
        (b"\x1b[4:1mA", crate::attrs::UnderlineStyle::Single),
        (b"\x1b[4:2mA", crate::attrs::UnderlineStyle::Double),
        (b"\x1b[4:3mA", crate::attrs::UnderlineStyle::Curly),
        (b"\x1b[4:4mA", crate::attrs::UnderlineStyle::Dotted),
        (b"\x1b[4:5mA", crate::attrs::UnderlineStyle::Dashed),
    ];
    for (input, expected) in cases {
        let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
        process(&mut parser, input);
        let cell = parser.screen().cell(0, 0).unwrap();
        assert_eq!(
            cell.attrs().underline_style(),
            expected,
            "input: {:?}",
            input
        );
    }
}

#[test]
fn sgr_21_double_underline() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[21mA");
    let cell = parser.screen().cell(0, 0).unwrap();
    assert_eq!(
        cell.attrs().underline_style(),
        crate::attrs::UnderlineStyle::Double
    );
}

#[test]
fn sgr_24_cancels_underline() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[4:3m\x1b[24mA");
    let cell = parser.screen().cell(0, 0).unwrap();
    assert!(!cell.attrs().underline());
    assert_eq!(
        cell.attrs().underline_style(),
        crate::attrs::UnderlineStyle::None
    );
}

#[test]
fn sgr_underline_color_rgb() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[4m\x1b[58:2:255:0:0mA");
    let cell = parser.screen().cell(0, 0).unwrap();
    assert!(cell.attrs().underline());
    assert_eq!(
        cell.attrs().underline_color,
        crate::attrs::Color::Rgb(255, 0, 0)
    );
}

#[test]
fn sgr_underline_color_rgb_with_colorspace() {
    // 58:2::R:G:B with empty color space ID (same format scoutty uses)
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[58:2::170:170:170mA");
    let cell = parser.screen().cell(0, 0).unwrap();
    assert_eq!(
        cell.attrs().underline_color,
        crate::attrs::Color::Rgb(170, 170, 170),
        "58:2::R:G:B format with color space ID should be parsed"
    );
}

#[test]
fn sgr_underline_color_indexed() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[4m\x1b[58;5;196mA");
    let cell = parser.screen().cell(0, 0).unwrap();
    assert_eq!(
        cell.attrs().underline_color,
        crate::attrs::Color::Index(196)
    );
}

#[test]
fn sgr_59_resets_underline_color() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[58;5;196m\x1b[59mA");
    let cell = parser.screen().cell(0, 0).unwrap();
    assert_eq!(cell.attrs().underline_color, crate::attrs::Color::Default);
}

#[test]
fn sgr_overline() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[53mX\x1b[55mY");
    let screen = parser.screen();
    assert!(screen.cell(0, 0).unwrap().attrs().overline());
    assert!(!screen.cell(0, 1).unwrap().attrs().overline());
}

#[test]
fn sgr_blink() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[5mX\x1b[25mY");
    let screen = parser.screen();
    assert!(screen.cell(0, 0).unwrap().attrs().blink());
    assert!(!screen.cell(0, 1).unwrap().attrs().blink());
}

#[test]
fn sgr_reset_clears_overline_and_blink() {
    let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
    process(&mut parser, b"\x1b[53;5mX\x1b[0mY");
    let screen = parser.screen();
    let x = screen.cell(0, 0).unwrap();
    assert!(x.attrs().overline());
    assert!(x.attrs().blink());
    let y = screen.cell(0, 1).unwrap();
    assert!(!y.attrs().overline());
    assert!(!y.attrs().blink());
}