119 lines
3.4 KiB
Rust
119 lines
3.4 KiB
Rust
//! Tests of the template segmenter.
|
|
|
|
use std::time::Instant;
|
|
|
|
use notescraft_templater::{Error, Kind, MAX_COMMANDS, MAX_TEMPLATE_BYTES, Segment, parse};
|
|
|
|
/// A text segment.
|
|
fn text(value: &str) -> Segment {
|
|
Segment::Text(value.to_owned())
|
|
}
|
|
|
|
/// An output command segment.
|
|
fn command(src: &str, line: u32) -> Segment {
|
|
Segment::Command { src: src.to_owned(), line, kind: Kind::Output }
|
|
}
|
|
|
|
/// The segments of a template that is expected to parse.
|
|
fn segments(src: &str) -> Vec<Segment> {
|
|
match parse(src) {
|
|
Ok(template) => template.segments,
|
|
Err(err) => panic!("parse failed: {err}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn plain_text_is_one_segment() {
|
|
assert_eq!(segments("hello world"), vec![text("hello world")]);
|
|
assert_eq!(segments(""), Vec::new());
|
|
}
|
|
|
|
#[test]
|
|
fn one_command() {
|
|
assert_eq!(segments("<% x %>"), vec![command("x", 1)]);
|
|
assert_eq!(segments("<% x %>"), vec![command("x", 1)]);
|
|
}
|
|
|
|
#[test]
|
|
fn three_commands_keep_their_lines() {
|
|
assert_eq!(segments("<% a %>\n\n<% b %>\n\n<% c %>"), vec![command("a", 1), text("\n\n"), command("b", 3), text("\n\n"), command("c", 5)]);
|
|
}
|
|
|
|
#[test]
|
|
fn minus_before_strips_every_whitespace() {
|
|
assert_eq!(segments("a \n <%- x %>"), vec![text("a"), command("x", 2)]);
|
|
}
|
|
|
|
#[test]
|
|
fn underscore_before_strips_spaces_and_tabs() {
|
|
assert_eq!(segments("a \n <%_ x %>"), vec![text("a \n"), command("x", 2)]);
|
|
}
|
|
|
|
#[test]
|
|
fn minus_after_strips_the_newline() {
|
|
assert_eq!(segments("<% x -%> \n b"), vec![command("x", 1), text(" b")]);
|
|
}
|
|
|
|
#[test]
|
|
fn underscore_after_strips_spaces_and_tabs() {
|
|
assert_eq!(segments("<% x _%> \n b"), vec![command("x", 1), text("\n b")]);
|
|
}
|
|
|
|
#[test]
|
|
fn a_javascript_command_is_kept_and_marked() {
|
|
assert_eq!(segments("<%* tR.now() %>"), vec![Segment::Command { src: "tR.now()".to_owned(), line: 1, kind: Kind::Js }]);
|
|
}
|
|
|
|
#[test]
|
|
fn an_unterminated_command_reports_its_line() {
|
|
match parse("a\nb\n<% x") {
|
|
Err(Error::Unterminated { line }) => assert_eq!(line, 3),
|
|
other => panic!("expected an unterminated command, got {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_close_marker_without_an_open_is_text() {
|
|
assert_eq!(segments("a %> b"), vec![text("a %> b")]);
|
|
}
|
|
|
|
#[test]
|
|
fn carriage_returns_survive_outside_the_stripped_areas() {
|
|
assert_eq!(segments("a\r\n<% x %>\r\nb"), vec![text("a\r\n"), command("x", 2), text("\r\nb")]);
|
|
}
|
|
|
|
#[test]
|
|
fn minus_after_strips_a_carriage_return_line_feed() {
|
|
assert_eq!(segments("<% x -%>\r\nb"), vec![command("x", 1), text("b")]);
|
|
}
|
|
|
|
#[test]
|
|
fn too_many_commands_are_refused() {
|
|
let src = "<% x %>".repeat(MAX_COMMANDS.saturating_add(1));
|
|
match parse(&src) {
|
|
Err(Error::TooManyCommands) => {}
|
|
other => panic!("expected too many commands, got {other:?}"),
|
|
}
|
|
let ok = "<% x %>".repeat(MAX_COMMANDS);
|
|
assert!(parse(&ok).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn a_template_over_one_mebibyte_is_refused() {
|
|
let src = "a".repeat(MAX_TEMPLATE_BYTES.saturating_add(1));
|
|
match parse(&src) {
|
|
Err(Error::TooLarge) => {}
|
|
other => panic!("expected a too large error, got {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_megabyte_of_open_markers_fails_fast() {
|
|
let src = "<%".repeat(MAX_TEMPLATE_BYTES / 2);
|
|
let start = Instant::now();
|
|
let err = parse(&src).unwrap_err();
|
|
let elapsed = start.elapsed();
|
|
assert!(matches!(err, Error::Unterminated { .. }), "expected an unterminated command, got {err:?}");
|
|
assert!(elapsed.as_millis() < 200, "took {elapsed:?}");
|
|
}
|