4.8 KiB · text History af1c436
//! Save/load round-tripping, and the shipped cbz preset documents.

use icon_studio::doc::{self, Session};
use icon_studio::palette::NamedColor;
use icon_studio::spec::{Background, Foreground, IconSpec};

fn sample() -> Session {
    Session {
        spec: IconSpec {
            foreground: Foreground::Icon('\u{e899}'),
            sub_banner: "beta".into(),
            fg_color: NamedColor {
                role: "base-200",
                rgb: [0x0D, 0x2A, 0x2C],
            },
            bg_a: NamedColor {
                role: "primary",
                rgb: [0x00, 0xC9, 0xD4],
            },
            bg_b: NamedColor {
                role: "accent",
                rgb: [0xAA, 0xD4, 0x00],
            },
            background: Background::Stripes,
            angle: 30.0,
            scale: 0.54,
            show_guides: true,
            filled: true,
        },
        palette: "Casabeza".into(),
        export_name: "ic_icon".into(),
        export_dir: "/tmp/x".into(),
        grid_masked: true,
        grid_colors: vec!["primary".into(), "accent".into()],
        grid_backgrounds: vec![Background::Solid, Background::Dots],
    }
}

#[test]
fn session_survives_a_round_trip() {
    let before = sample();
    let text = doc::to_toml(&before.to_document(|_| "lock".into())).unwrap();
    let after = Session::from_document(&doc::from_toml(&text).unwrap()).unwrap();
    assert_eq!(before, after);
}

#[test]
fn documents_are_toml_not_json() {
    let text = doc::to_toml(&sample().to_document(|_| "lock".into())).unwrap();
    assert!(
        text.contains("[colors.foreground]"),
        "expected TOML tables:\n{text}"
    );
    assert!(
        text.contains("rgb = [13, 42, 44]"),
        "rgb should be inline:\n{text}"
    );
    assert!(
        text.contains("scale = 0.54"),
        "floats should stay tidy:\n{text}"
    );
}

#[test]
fn a_document_from_the_future_is_refused() {
    let mut d = sample().to_document(|_| String::new());
    d.version = doc::VERSION + 1;
    let err = Session::from_document(&d).unwrap_err();
    assert!(err.contains("version"), "{err}");
}

#[test]
fn an_unknown_background_is_an_error_not_a_silent_default() {
    let mut d = sample().to_document(|_| String::new());
    d.background.style = "hexagons".into();
    assert!(Session::from_document(&d).unwrap_err().contains("hexagons"));
}

#[test]
fn an_unknown_colour_role_falls_back_to_the_stored_rgb() {
    // A theme may drop a role; the document should still open, with the
    // literal colour it was saved with.
    let mut d = sample().to_document(|_| String::new());
    d.colors.background_a.role = "no-such-role".into();
    let s = Session::from_document(&d).unwrap();
    assert_eq!(s.spec.bg_a.rgb, [0x00, 0xC9, 0xD4]);
}

#[test]
fn a_known_role_is_rebound_to_the_current_palette() {
    // The role wins over the stored rgb, so retuning a theme updates documents
    // that reference it.
    let mut d = sample().to_document(|_| String::new());
    d.colors.background_a.rgb = [1, 2, 3];
    let s = Session::from_document(&d).unwrap();
    assert_eq!(
        s.spec.bg_a.rgb,
        [0x00, 0xC9, 0xD4],
        "should follow Casabeza primary"
    );
}

/// The five presets must load, and must still reproduce the backgrounds of the
/// icons they were derived from.
#[test]
fn cbz_presets_load_and_match_the_shipped_backgrounds() {
    const TEAL: [u8; 3] = [0x00, 0xC9, 0xD4];
    const GREEN: [u8; 3] = [0xAA, 0xD4, 0x00];

    for stem in [
        "cbz-auth",
        "cbz-lists",
        "cbz-numerics",
        "cbz-scanner",
        "cbz-inventory",
    ] {
        let path = format!("presets/{stem}.toml");
        let d = doc::load(std::path::Path::new(&path)).unwrap_or_else(|e| panic!("{path}: {e}"));
        let s = Session::from_document(&d).unwrap_or_else(|e| panic!("{path}: {e}"));

        assert_eq!(s.palette, "Casabeza");
        assert_eq!(s.spec.background, Background::DiagonalSplit);

        let bg = icon_studio::render::render_background(&s.spec, 432);
        let corner = |x: u32, y: u32| {
            let p = bg.pixel(x, y).unwrap();
            [p.red(), p.green(), p.blue()]
        };
        // Matches the orientation measured off the shipped assets: green in
        // the upper right, teal in the lower left.
        assert_eq!(corner(411, 20), GREEN, "{stem}: top-right");
        assert_eq!(corner(20, 411), TEAL, "{stem}: bottom-left");

        // And the mark renders something.
        let fg = icon_studio::render::render_foreground(&s.spec, 432);
        assert!(
            fg.pixels().iter().any(|p| p.alpha() > 0),
            "{stem}: empty foreground"
        );
    }
}

#[test]
fn numerics_preset_is_the_text_variant() {
    let d = doc::load(std::path::Path::new("presets/cbz-numerics.toml")).unwrap();
    let s = Session::from_document(&d).unwrap();
    assert_eq!(s.spec.foreground, Foreground::Text("1234".into()));
}