mirror of
https://git.pumpin.online/funkpower/typikon.git
synced 2026-10-22 18:57:13 +00:00
96 lines
3.3 KiB
Rust
96 lines
3.3 KiB
Rust
//! Every title the program can print, at every width it can be asked to print
|
|||
|
|
//! at. The seven-day view cuts titles to fit, and the titles are full of em
|
||
|
|
//! dashes: a cut taken at a byte index rather than a character one panics, and
|
||
|
|
//! did, at 80 columns.
|
||
|
|
|
||
|
|
use std::process::Command;
|
||
|
|
|
||
|
|
use typikon::calendars::Date;
|
||
|
|
use typikon::day::week;
|
||
|
|
use typikon::jurisdictions::{CALENDARS, JURISDICTIONS};
|
||
|
|
|
||
|
|
/// The same cut the seven-day view makes. Kept in step with `main.rs::elide`
|
||
|
|
/// by exercising the binary below as well.
|
||
|
|
fn elide(text: &str, width: usize) -> String {
|
||
|
|
if width == 0 {
|
||
|
|
return String::new();
|
||
|
|
}
|
||
|
|
if text.chars().count() <= width {
|
||
|
|
return text.to_string();
|
||
|
|
}
|
||
|
|
let cut: String = text.chars().take(width - 1).collect();
|
||
|
|
let midpoint = cut
|
||
|
|
.char_indices()
|
||
|
|
.nth(cut.chars().count() / 2)
|
||
|
|
.map_or(cut.len(), |(i, _)| i);
|
||
|
|
let head = match cut[midpoint..].find(' ') {
|
||
|
|
Some(_) => match cut.trim_end().rfind(' ') {
|
||
|
|
Some(i) => &cut[..i],
|
||
|
|
None => &cut,
|
||
|
|
},
|
||
|
|
None => &cut,
|
||
|
|
};
|
||
|
|
format!("{}\u{2026}", head.trim_end_matches([' ', ',', ';']))
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn no_title_of_any_year_can_be_cut_at_any_width_without_panicking() {
|
||
|
|
let start = Date::from_ymd(2020, 1, 1).unwrap();
|
||
|
|
for jur in JURISDICTIONS.iter().chain(CALENDARS.iter()) {
|
||
|
|
for day in week(start, 366 * 11, jur) {
|
||
|
|
for width in 0..=120 {
|
||
|
|
let out = elide(day.title(), width);
|
||
|
|
assert!(
|
||
|
|
out.chars().count() <= width.max(1),
|
||
|
|
"{}: {width} gave {} chars",
|
||
|
|
day.date,
|
||
|
|
out.chars().count()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn a_cut_never_lands_inside_a_character() {
|
||
|
|
// Every movable feast title carries an em dash, which is where this broke.
|
||
|
|
for title in [
|
||
|
|
"The Radiant and Life-giving Resurrection of our Lord \u{2014} Holy Pascha",
|
||
|
|
"Holy Pentecost \u{2014} the Descent of the Holy Spirit",
|
||
|
|
"Clean Monday \u{2014} the first day of the Great Fast",
|
||
|
|
"Meatfare Sunday \u{2014} the Sunday of the Last Judgement",
|
||
|
|
"\u{2014}\u{2014}\u{2014}\u{2014}\u{2014}\u{2014}\u{2014}\u{2014}",
|
||
|
|
] {
|
||
|
|
for width in 0..=80 {
|
||
|
|
let out = elide(title, width);
|
||
|
|
assert!(out.is_char_boundary(0));
|
||
|
|
assert!(std::str::from_utf8(out.as_bytes()).is_ok());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn the_binary_survives_every_terminal_width() {
|
||
|
|
let binary = concat!(env!("CARGO_MANIFEST_DIR"), "/target/release/typikon");
|
||
|
|
if !std::path::Path::new(binary).exists() {
|
||
|
|
eprintln!("skipping: {binary} not built");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
for width in [1, 20, 40, 60, 76, 78, 80, 82, 88, 100, 120, 200] {
|
||
|
|
for jur in ["new", "rocor", "ruthenian", "goarch"] {
|
||
|
|
let out = Command::new(binary)
|
||
|
|
.args(["-j", jur, "--date", "2026-01-01", "--days", "365"])
|
||
|
|
.env("COLUMNS", width.to_string())
|
||
|
|
.env_remove("TYPIKON_JURISDICTION")
|
||
|
|
.output()
|
||
|
|
.expect("the binary runs");
|
||
|
|
assert!(
|
||
|
|
out.status.success(),
|
||
|
|
"COLUMNS={width} -j {jur}: exit {:?}\n{}",
|
||
|
|
out.status.code(),
|
||
|
|
String::from_utf8_lossy(&out.stderr)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|