//! 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, Layer};
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,
mark_size: 0.9,
mark_rotation: -15.0,
mark_offset: [0.0, 0.25],
show_guides: true,
filled: true,
layers: vec![Layer {
glyph: '\u{e86c}',
color: Some(NamedColor {
role: "accent",
rgb: [0xAA, 0xD4, 0x00],
}),
size: 0.3,
rotation: 45.0,
offset: [-0.5, 0.125],
filled: false,
}],
},
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 layers_are_written_as_an_array_of_tables() {
let text = doc::to_toml(&sample().to_document(|_| "lock".into())).unwrap();
assert!(text.contains("[[layers]]"), "{text}");
assert!(text.contains("codepoint = \"e86c\""), "{text}");
assert!(
text.contains("size = 0.3\n"),
"floats should stay tidy:\n{text}"
);
}
#[test]
fn a_single_mark_document_has_no_layers_section() {
// Older documents and single-mark ones should read exactly as before.
let mut s = sample();
s.spec.layers.clear();
let text = doc::to_toml(&s.to_document(|_| "lock".into())).unwrap();
assert!(!text.contains("layers"), "{text}");
let back = Session::from_document(&doc::from_toml(&text).unwrap()).unwrap();
assert!(back.spec.layers.is_empty());
}
#[test]
fn a_centred_unrotated_mark_writes_no_placement() {
// rotation and offset only appear once they are used, so presets and
// other plain documents are unchanged by their existence.
let mut s = sample();
s.spec.mark_rotation = 0.0;
s.spec.mark_offset = [0.0, 0.0];
s.spec.layers.clear();
let text = doc::to_toml(&s.to_document(|_| "lock".into())).unwrap();
assert!(!text.contains("rotation"), "{text}");
assert!(!text.contains("offset"), "{text}");
}
#[test]
fn a_layer_following_the_foreground_writes_no_colour() {
let mut s = sample();
s.spec.layers[0].color = None;
let text = doc::to_toml(&s.to_document(|_| "lock".into())).unwrap();
assert!(!text.contains("[layers.color]"), "{text}");
let back = Session::from_document(&doc::from_toml(&text).unwrap()).unwrap();
assert_eq!(back.spec.layers[0].color, None);
assert_eq!(
back.spec.layer_color(&back.spec.layers[0]),
back.spec.fg_color
);
}
#[test]
fn a_full_size_mark_writes_no_size() {
// mark.size only appears once it differs from what older documents mean.
let mut s = sample();
s.spec.mark_size = 1.0;
let text = doc::to_toml(&s.to_document(|_| "lock".into())).unwrap();
assert!(!text.contains("\nsize = 1"), "{text}");
let back = Session::from_document(&doc::from_toml(&text).unwrap()).unwrap();
assert_eq!(back.spec.mark_size, 1.0);
}
#[test]
fn a_bad_layer_codepoint_is_an_error() {
let mut d = sample().to_document(|_| String::new());
d.layers[0].codepoint = "zz".into();
assert!(Session::from_document(&d).unwrap_err().contains("zz"));
}
#[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()));
}
neiam
/icon-studio
public · Issues · Pulls · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
7.6 KiB · text
History
aca7335