Files
ELYTerminal/src/term/notify/tests.rs
T

236 lines
7.4 KiB
Rust

use super::*;
fn shown(ask: Option<Ask>) -> Notice {
match ask {
Some(Ask::Show(notice)) => notice,
other => panic!("no notification: {other:?}"),
}
}
#[test]
fn osc_9_is_iterm2_s_notification_unless_it_is_conemu_s_command() {
assert_eq!(shown(nine(b"Hello world")).title, "Hello world");
for command in [
&b"1;100"[..],
b"10",
b"10;2",
b"11;note",
b"12",
b"2;box",
b"3;",
b"3;title",
b"5",
b"5x",
b"6;macro",
b"7;cmd",
b"8;VAR",
b"9;/path",
] {
assert_eq!(nine(command), None, "{}", String::from_utf8_lossy(command));
}
for text in [
"1", "1a", "10x", "10;5", "13", "2", "2a", "4", "4;", "4;9", "x;y",
] {
assert_eq!(shown(nine(text.as_bytes())).title, text);
}
assert_eq!(nine(b""), None);
assert_eq!(nine(b"a\xffb"), None, "not UTF-8");
}
#[test]
fn osc_9_4_reads_progress_as_ghostty_does() {
let progress = |body: &[u8]| match nine(body) {
Some(Ask::Progress(progress)) => progress,
other => panic!("no progress: {other:?}"),
};
assert_eq!(progress(b"4;0"), Progress::Remove);
assert_eq!(progress(b"4;1"), Progress::Set(Some(0)));
assert_eq!(progress(b"4;1;40"), Progress::Set(Some(40)));
assert_eq!(progress(b"4;1;900"), Progress::Set(Some(100)));
assert_eq!(progress(b"4;1;x"), Progress::Set(None));
assert_eq!(
progress(b"4;1;99999999999999999999999"),
Progress::Set(None)
);
assert_eq!(progress(b"4;1;1_0"), Progress::Set(None));
assert_eq!(progress(b"4;2"), Progress::Error(None));
assert_eq!(progress(b"4;2;50"), Progress::Error(Some(50)));
assert_eq!(progress(b"4;3;7"), Progress::Indeterminate);
assert_eq!(progress(b"4;4;10"), Progress::Pause(Some(10)));
}
#[test]
fn osc_777_is_rxvt_s_notify() {
let notice = shown(rxvt(b"notify;Title;Body;more"));
assert_eq!(
(notice.title.as_str(), notice.body.as_str()),
("Title", "Body;more")
);
assert_eq!(rxvt(b"other;x;y"), None);
assert_eq!(rxvt(b"notify;Title"), None, "no body");
assert_eq!(rxvt(b"notify;;"), None, "nothing to say");
}
#[test]
fn osc_99_gathers_chunks_by_id_and_reads_its_options() {
let mut kitty = Kitty::default();
assert_eq!(kitty.read(b"i=a:d=0;Hel"), None);
assert_eq!(kitty.read(b"i=b:d=0:p=body;other"), None);
assert_eq!(kitty.read(b"i=a:d=0:p=body;Wor"), None);
let notice = shown(kitty.read(b"i=a:p=body:o=unfocused:a=report,-focus:c=1:w=5000;ld"));
assert_eq!(
(
notice.id.as_deref(),
notice.title.as_str(),
notice.body.as_str()
),
(Some("a"), "Hel", "World")
);
assert_eq!(
(notice.focus, notice.report, notice.close, notice.expire),
(false, true, true, Some(5000)),
"o passes by"
);
let other = shown(kitty.read(b"i=b:p=body;!"));
assert_eq!(
(other.title.as_str(), other.body.as_str()),
("other!", ""),
"a lone body is the title"
);
}
#[test]
fn osc_99_decodes_base64_and_takes_plain_text() {
let mut kitty = Kitty::default();
let notice = shown(kitty.read(b"e=1:p=body;SGVsbG8gd29ybGQ="));
assert_eq!((notice.title.as_str(), notice.id), ("Hello world", None));
let plain = shown(kitty.read(b";Hello world"));
assert_eq!(
(plain.title.as_str(), plain.focus, plain.report),
("Hello world", true, false)
);
assert_eq!(
kitty.read(b"bad:i=c;x").map(|_| ()),
Some(()),
"a malformed pair passes by"
);
}
#[test]
fn osc_99_gathers_base64_chunked_before_or_after_encoding() {
let title = |chunks: &[&str]| {
let mut kitty = Kitty::default();
let (last, first) = chunks.split_last().expect("a chunk");
for chunk in first {
assert_eq!(kitty.read(format!("i=a:d=0:e=1;{chunk}").as_bytes()), None);
}
shown(kitty.read(format!("i=a:e=1;{last}").as_bytes())).title
};
assert_eq!(title(&["SGVsb", "G8="]), "Hello", "split inside a group");
assert_eq!(title(&["8J+Y", "gA=="]), "😀", "split inside a character");
assert_eq!(
title(&["SGVsbG8=", "IHdvcmxk"]),
"Hello world",
"each chunk padded"
);
assert_eq!(title(&["SGVsbG", "8"]), "Hello", "the last chunk unpadded");
let mut kitty = Kitty::default();
assert_eq!(kitty.read(b"i=n:d=0;Title"), None);
let notice = shown(kitty.read(b"i=n:p=body:e=1;TGluZSAxCkxpbmUgMg=="));
assert_eq!(
(notice.title.as_str(), notice.body.as_str()),
("Title", "Line 1\nLine 2"),
"base64 carries any UTF-8"
);
}
#[test]
fn osc_99_asks_to_close_and_what_is_alive_and_supported() {
let mut kitty = Kitty::default();
assert_eq!(kitty.read(b"i=x:p=close;"), Some(Ask::Close("x".into())));
assert_eq!(kitty.read(b"p=close;"), None, "no id, no close");
assert_eq!(kitty.read(b"i=q:p=?;"), Some(Ask::Query("q".into())));
assert_eq!(kitty.read(b"p=?;"), Some(Ask::Query("0".into())));
assert_eq!(kitty.read(b"i=y:p=alive;"), Some(Ask::Alive("y".into())));
assert_eq!(
kitty.read(b"p=icon;x"),
None,
"an unknown payload passes by"
);
}
#[test]
fn osc_99_drops_what_breaks_its_rules() {
let mut kitty = Kitty::default();
let long = |length: usize, metadata: &str| {
let mut body = format!("{metadata};").into_bytes();
body.extend(std::iter::repeat_n(b'a', length));
body
};
for body in [
b"no payload".to_vec(),
b"i=a b;x".to_vec(),
b"i=a\"b;x".to_vec(),
b"e=1;!!!".to_vec(),
b"e=1;/w==".to_vec(),
"x\u{85}y"
.as_bytes()
.iter()
.copied()
.fold(b";".to_vec(), |mut body, byte| {
body.push(byte);
body
}),
b";a\tb".to_vec(),
b"e=1;SGVsbG8gS".to_vec(),
long(PLAIN + 1, ""),
long(ENCODED + 4, "e=1"),
long(1, &"x".repeat(METADATA + 1)),
] {
assert_eq!(
kitty.read(&body),
None,
"{}",
String::from_utf8_lossy(&body)
);
}
assert_eq!(kitty.read(b"i=k:d=0;Hel"), None);
assert_eq!(kitty.read(b"i=k:d=0:e=1;!!!!"), None);
assert!(kitty.pending.is_empty(), "a bad chunk drops what gathered");
for at in 0..PENDING {
assert_eq!(kitty.read(format!("i=p{at}:d=0;x").as_bytes()), None);
}
assert_eq!(kitty.read(b"i=over:d=0;x"), None);
assert!(
kitty.pending.iter().all(|(id, _)| id != "over"),
"past the cap nothing gathers"
);
let mut big = Kitty::default();
for _ in 0..GATHERED / PLAIN {
assert_eq!(big.read(&long(PLAIN, "i=g:d=0")), None);
}
assert_eq!(
big.read(&long(PLAIN, "i=g")),
None,
"over the gathered bound"
);
assert!(big.pending.is_empty());
}
#[test]
fn the_answers_name_the_asker_s_id() {
let offered = if CLOSES {
"p=title,body,close,?,alive:w=1"
} else {
"p=title,body,?,alive"
};
assert_eq!(
answer("q"),
format!("\x1b]99;i=q:p=?;a=focus,report:o=invisible:{offered}\x1b\\")
);
assert_eq!(alive("0", &[]), "\x1b]99;i=0:p=alive;\x1b\\");
assert_eq!(alive("z", &["a", "b"]), "\x1b]99;i=z:p=alive;a,b\x1b\\");
assert_eq!(untracked("n1"), "\x1b]99;i=n1:p=close;untracked\x1b\\");
assert_eq!(activated("0"), "\x1b]99;i=0;\x1b\\");
}