use std::time::{Duration, Instant};
fn long_flow_line(n: usize) -> String {
let inner = format!("[{}a]", "a,".repeat(999));
format!("[{}]\n", vec![inner; n / 1000].join(","))
}
fn fastest(runs: usize, mut f: impl FnMut()) -> Duration {
(0..runs)
.map(|_| {
let t = Instant::now();
f();
t.elapsed()
})
.min()
.unwrap_or_default()
}
fn assert_linear(entry: &str, parse: fn(&str)) {
let small = long_flow_line(10_000);
let large = long_flow_line(80_000);
let t_small = fastest(5, || parse(&small));
let t_large = fastest(3, || parse(&large));
let t_small = t_small.max(Duration::from_micros(200));
let ratio = t_large.as_secs_f64() / t_small.as_secs_f64();
assert!(
ratio < 24.0,
"{entry}: 8x longer line took {ratio:.1}x as long \
({t_small:?} -> {t_large:?}); expected linear growth",
);
}
#[test]
fn long_flow_line_parses_in_linear_time_value() {
assert_linear("from_str::<Value>", |s| {
let _ = noyalib::from_str::<noyalib::Value>(s).expect("valid input");
});
}
#[test]
fn long_flow_line_parses_in_linear_time_borrowed() {
assert_linear("borrowed::from_str_borrowed", |s| {
let _ = noyalib::borrowed::from_str_borrowed(s).expect("valid input");
});
}
#[cfg(feature = "std")]
#[test]
fn long_flow_line_parses_in_linear_time_cst() {
assert_linear("cst::parse_document", |s| {
let _ = noyalib::cst::parse_document(s).expect("valid input");
});
}
#[test]
fn max_events_refuses_a_ten_megabyte_flow_collection() {
use noyalib::{BudgetBreach, Error, ParserConfig, Value};
let cfg = ParserConfig::default()
.max_document_length(64 * 1024 * 1024)
.max_events(10);
let one_line = format!("[{}a]\n", "a,".repeat(5 * 1024 * 1024));
let many_lines = format!("[{}a]\n", "a,\n".repeat(3_500_000));
for input in [&one_line, &many_lines] {
match noyalib::from_str_with_config::<Value>(input, &cfg) {
Err(Error::Budget(BudgetBreach::MaxEvents { limit: 10, .. })) => {}
other => panic!("expected the max_events budget, got {other:?}"),
}
}
}