use std::fmt::Write as _;
use monospace_core::{Buffer, Pos, Size, Window};
use monospace_description::parse;
const DRAWING_STROKES: [&str; 5] = ["ascii", "light", "light-round", "heavy", "double"];
const KINDS: [&str; 3] = ["box", "line", "connector"];
const SIZE: Size = Size {
width: 12,
height: 7,
};
fn description_of(shapes: &str) -> String {
format!(
r#"{{
"next_id": 3,
"shapes": [ {shapes} ]
}}"#
)
}
fn case(name: &str, shapes: &str) -> String {
let description = description_of(shapes);
let diagram =
parse(&description).unwrap_or_else(|error| panic!("{name} is not readable: {error}"));
let mut buffer = Buffer::new(Window {
origin: Pos { x: 0, y: 0 },
size: SIZE,
});
diagram.draw(&mut buffer);
let rendered = monospace_core::render(&buffer, &monospace_glyph_sets::all(), buffer.window());
let mut out = String::new();
let _ = writeln!(out, "{name}");
for line in rendered.lines() {
let _ = writeln!(out, "{}", line.trim_end());
}
out
}
fn pin(name: &str, cases: &[String]) {
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/snapshots/characterization"
));
settings.set_description(concat!(
"This file records what the code does over a range too wide to assert by hand. ",
"A change here is the consequence of a decision taken elsewhere, not a decision. ",
"It is accepted after a report of how many cases moved, in which families, and ",
"three examples with their before and after.",
));
let joined = cases.join("\n");
settings.bind(|| insta::assert_snapshot!(name, joined));
}
#[test]
fn every_shape_in_every_stroke_it_can_be_drawn_in() {
let mut by_kind: Vec<(String, Vec<String>)> = Vec::new();
for kind in KINDS {
let mut cases = Vec::new();
for stroke in DRAWING_STROKES {
let shapes = match kind {
"box" => format!(
r#"{{ "kind": "box", "id": 1, "at": {{ "x": 3, "y": 2 }},
"size": {{ "width": 5, "height": 3 }}, "stroke": "{stroke}" }}"#
),
"line" => format!(
r#"{{ "kind": "line", "id": 1, "at": {{ "x": 1, "y": 3 }}, "len": 9,
"orientation": "horizontal", "stroke": "{stroke}" }}"#
),
_ => format!(
r#"{{ "kind": "connector", "id": 1,
"from": {{ "at": {{ "kind": "point", "x": 1, "y": 3 }},
"leaving": "right",
"terminal": {{ "kind": "arm" }} }},
"to": {{ "at": {{ "kind": "point", "x": 10, "y": 3 }},
"leaving": "left",
"terminal": {{ "kind": "arm" }} }},
"stroke": "{stroke}" }}"#
),
};
cases.push(case(&format!("{kind} in {stroke}"), &shapes));
}
by_kind.push((format!("every_{kind}_in_every_stroke"), cases));
}
for (name, cases) in by_kind {
pin(&name, &cases);
}
}
#[test]
fn two_shapes_crossing_in_every_ordered_pair_of_strokes() {
for front in DRAWING_STROKES {
let mut cases = Vec::new();
for behind in DRAWING_STROKES {
let arrangements: [(String, String); 2] = [
(front.to_owned(), behind.to_owned()),
(behind.to_owned(), front.to_owned()),
];
let mut seen = Vec::new();
for (front_stroke, behind_stroke) in arrangements {
let name = format!("{front_stroke} over {behind_stroke}");
if seen.contains(&name) {
continue;
}
seen.push(name.clone());
let shapes = format!(
r#"{{ "kind": "box", "id": 1, "at": {{ "x": 4, "y": 1 }},
"size": {{ "width": 5, "height": 5 }}, "stroke": "{front_stroke}" }},
{{ "kind": "line", "id": 2, "at": {{ "x": 2, "y": 3 }}, "len": 9,
"orientation": "horizontal", "stroke": "{behind_stroke}" }}"#
);
cases.push(case(&name, &shapes));
}
}
pin(&format!("crossing_{front}_over_every_stroke"), &cases);
}
}
#[test]
fn the_sweep_covers_its_range_whole() {
assert_eq!(KINDS.len() * DRAWING_STROKES.len(), 15);
let n = DRAWING_STROKES.len();
assert_eq!(n * (2 * (n - 1) + 1), 45);
assert!(
!DRAWING_STROKES
.iter()
.any(|stroke| stroke.starts_with("light-round-")
|| *stroke == "light-double"
|| *stroke == "light-heavy"),
"the mixing tables are not drawing tables and must not appear in the range"
);
}