use std::process::{Command, Stdio};
fn ae() -> std::path::PathBuf {
let mut p = std::env::current_exe().expect("test exe");
p.pop();
if p.ends_with("deps") {
p.pop();
}
p.join(format!("ae{}", std::env::consts::EXE_SUFFIX))
}
fn run(code: &str) -> (bool, String) {
let out = Command::new(ae())
.args(["-c", code])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("run ae");
let merged = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
(out.status.success(), merged)
}
fn assert_no_panic(label: &str, code: &str) {
let (_, out) = run(code);
assert!(
!out.contains("panicked"),
"{label} panicked the shell:\n{out}"
);
assert!(
!out.contains("char boundary"),
"{label} hit a char-boundary slice:\n{out}"
);
}
#[test]
fn a_long_box_drawing_rule_does_not_panic() {
for ch in ['─', '═', '━', '·'] {
let rule: String = std::iter::repeat_n(ch, 60).collect();
assert_no_panic(
&format!("a 60-char rule of {ch:?}"),
&format!("print(\"{rule}\")"),
);
}
}
#[test]
fn multibyte_characters_at_every_offset_around_the_limit() {
for pad in 70..90 {
let s = format!("{}★ tail", "a".repeat(pad));
assert_no_panic(
&format!("{pad} ASCII then a 3-byte char"),
&format!("print(\"{s}\")"),
);
}
}
#[test]
fn emoji_and_cjk_do_not_panic() {
let emoji: String = std::iter::repeat_n('🎨', 40).collect();
assert_no_panic("40 emoji", &format!("print(\"{emoji}\")"));
let cjk: String = std::iter::repeat_n('漢', 50).collect();
assert_no_panic("50 CJK characters", &format!("print(\"{cjk}\")"));
}
#[test]
fn long_non_ascii_inside_containers_does_not_panic() {
let rule: String = std::iter::repeat_n('═', 50).collect();
assert_no_panic(
"array of rules",
&format!("print([\"{rule}\", \"{rule}\"])"),
);
assert_no_panic(
"record with a rule",
&format!("print({{banner: \"{rule}\"}})"),
);
}
#[test]
fn truncation_counts_characters_not_bytes() {
let cjk: String = std::iter::repeat_n('漢', 50).collect();
let (ok, out) = run(&format!("print(\"{cjk}\")"));
assert!(ok, "printing 50 CJK characters failed: {out}");
assert!(
!out.contains('…'),
"50 characters were truncated as though they were 150: {out}"
);
}