107 lines
3.2 KiB
Rust
107 lines
3.2 KiB
Rust
//! Integration tests for the media timestamp parser (`[[note.mp3#t=04:12]]`).
|
|
|
|
use notescraft_markdown::{MEDIA_EXTS, TimeFragment, format_time, is_media_ext, parse_time_fragment};
|
|
|
|
/// Start offset of a subpath, if it is a time fragment.
|
|
fn start(subpath: &str) -> Option<u64> {
|
|
parse_time_fragment(subpath).map(|frag| frag.start_ms)
|
|
}
|
|
|
|
#[test]
|
|
fn parses_the_documented_forms() {
|
|
assert_eq!(start("t=04:12"), Some(252_000));
|
|
assert_eq!(start("t=4:12"), Some(252_000));
|
|
assert_eq!(start("t=252"), Some(252_000));
|
|
assert_eq!(start("t=1:02:03"), Some(3_723_000));
|
|
assert_eq!(start("t=0:04:12.5"), Some(252_500));
|
|
assert_eq!(start("t=npt:10.5"), Some(10_500));
|
|
assert_eq!(start("t=10.5"), Some(10_500));
|
|
assert_eq!(start("t=0"), Some(0));
|
|
}
|
|
|
|
#[test]
|
|
fn parses_a_range() {
|
|
assert_eq!(parse_time_fragment("t=10,20"), Some(TimeFragment { start_ms: 10_000, end_ms: Some(20_000) }));
|
|
assert_eq!(parse_time_fragment("t=npt:10.5,20"), Some(TimeFragment { start_ms: 10_500, end_ms: Some(20_000) }));
|
|
assert_eq!(parse_time_fragment("t=10"), Some(TimeFragment { start_ms: 10_000, end_ms: None }));
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_a_range_that_does_not_move_forward() {
|
|
assert_eq!(parse_time_fragment("t=20,10"), None);
|
|
assert_eq!(parse_time_fragment("t=10,10"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_invalid_subpaths() {
|
|
for bad in [
|
|
"t=",
|
|
"t=-1",
|
|
"t=1e3",
|
|
"t=1.2345",
|
|
"t=99:99",
|
|
"t=1:75",
|
|
"t=360001",
|
|
"t= 10",
|
|
"t=10 ",
|
|
"t=10&x=1",
|
|
"t=10,20,30",
|
|
"t=10:",
|
|
"t=:10",
|
|
"t=1.2.3",
|
|
"t=10.",
|
|
"t=.5",
|
|
"page=3",
|
|
"T=10",
|
|
"",
|
|
] {
|
|
assert_eq!(parse_time_fragment(bad), None, "{bad} is not a time fragment");
|
|
}
|
|
assert_eq!(parse_time_fragment("t=\u{661}\u{662}"), None, "non-ASCII digits are not digits");
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_subpaths_over_the_byte_limit() {
|
|
let long = format!("t={}", "0".repeat(63));
|
|
assert_eq!(long.len(), 65);
|
|
assert_eq!(parse_time_fragment(&long), None);
|
|
}
|
|
|
|
#[test]
|
|
fn accepts_the_total_limit() {
|
|
assert_eq!(start("t=100:00:00"), Some(360_000_000));
|
|
assert_eq!(start("t=100:00:00.001"), None);
|
|
assert_eq!(start("t=6000:00"), Some(360_000_000));
|
|
assert_eq!(start("t=6000:00.001"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn formats_times() {
|
|
assert_eq!(format_time(0), "0:00");
|
|
assert_eq!(format_time(252_000), "4:12");
|
|
assert_eq!(format_time(3_723_000), "1:02:03");
|
|
assert_eq!(format_time(3_600_000), "1:00:00");
|
|
assert_eq!(format_time(59_999), "0:59", "seconds are truncated, not rounded");
|
|
assert_eq!(format_time(3_723_999), "1:02:03");
|
|
}
|
|
|
|
#[test]
|
|
fn formats_and_parses_round_trip() {
|
|
for step in 0..1000_u64 {
|
|
let ms = step * 1000;
|
|
let text = format!("t={}", format_time(ms));
|
|
assert_eq!(start(&text), Some(ms), "{text} does not round trip");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn media_extensions_are_case_insensitive() {
|
|
assert!(is_media_ext("MP3"));
|
|
assert!(is_media_ext("mp4"));
|
|
assert!(is_media_ext("MKV"));
|
|
assert!(!is_media_ext("md"));
|
|
assert!(!is_media_ext(""));
|
|
assert!(MEDIA_EXTS.contains(&"mp3"));
|
|
assert!(MEDIA_EXTS.contains(&"mp4"));
|
|
}
|