#![cfg(feature = "alloc")]
use tomlproc::{parse, to_string, to_string_pretty};
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 ^= self.0 >> 12;
self.0 ^= self.0 << 25;
self.0 ^= self.0 >> 27;
self.0.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
fn below(&mut self, bound: usize) -> usize {
(self.next() % bound as u64) as usize
}
fn pick<T: Copy>(&mut self, choices: &[T]) -> T {
choices[self.below(choices.len())]
}
}
const PIECES: &[&str] = &[
"a",
"b.c",
"\"q\"",
"'l'",
"=",
" ",
"\t",
"\n",
"\r\n",
"\r",
"#c",
".",
",",
"[",
"]",
"[[",
"]]",
"{",
"}",
"\"",
"'",
"\"\"\"",
"'''",
"\\",
"\\u00e9",
"\\U0001F600",
"\\q",
"1",
"0x1",
"0b_",
"1_0",
"01",
"1.",
".5",
"1e",
"+",
"-",
"inf",
"nan",
"true",
"false",
"1979-05-27",
"07:32:00",
"1979-05-27T07:32:00Z",
"1979-05-27 07:32:00",
"é",
"\u{1}",
"\u{feff}",
"\u{7f}",
"9223372036854775808",
"x = ",
"[t]",
"[[t]]",
"\\x41",
"\\xzz",
"\\e",
"07:32",
"2010-02-03 14:15",
"{ a = 1, }",
"{\n",
",\n}",
];
#[test]
fn never_panics_and_round_trips() {
let mut rng = Rng(0x5EED_1979_0527_0732);
for _ in 0..40_000 {
let mut input = String::new();
for _ in 0..rng.below(24) {
input.push_str(rng.pick(PIECES));
}
let Ok(table) = parse(&input) else { continue };
let written = to_string(&table);
let reparsed = parse(&written)
.unwrap_or_else(|e| panic!("input {input:?} wrote {written:?} which failed: {e}"));
assert_eq!(written, to_string(&reparsed), "input {input:?}");
let pretty = to_string_pretty(&table);
let reparsed = parse(&pretty).unwrap_or_else(|e| {
panic!("input {input:?} pretty-wrote {pretty:?} which failed: {e}")
});
assert_eq!(
written,
to_string(&reparsed),
"input {input:?} pretty-wrote {pretty:?}"
);
}
}
#[test]
fn never_panics_on_arbitrary_bytes() {
let mut rng = Rng(0x1234_5678_9ABC_DEF0);
for _ in 0..40_000 {
let bytes: Vec<u8> = (0..rng.below(48)).map(|_| rng.below(256) as u8).collect();
if let Ok(input) = str::from_utf8(&bytes) {
let _ = parse(input);
}
}
}
#[test]
fn truncations_of_a_real_document_never_panic() {
let document = include_str!("../README.md");
for end in 0..document.len() {
if document.is_char_boundary(end) {
let _ = parse(&document[..end]);
}
}
}
#[test]
fn spans_are_always_valid_slices() {
let mut rng = Rng(0x5A11_0000_1979_0527);
for _ in 0..20_000 {
let mut input = String::new();
for _ in 0..rng.below(24) {
input.push_str(rng.pick(PIECES));
}
let plain = parse(&input);
let spanned = tomlproc::parse_spans(&input);
assert_eq!(plain.is_ok(), spanned.is_ok(), "input {input:?}");
let Ok((table, spans)) = spanned else {
continue;
};
for (path, span) in spans.iter() {
let text = &input[span.value.clone()];
let whole = &input[span.range.clone()];
assert!(whole.contains(text), "input {input:?}: {path}");
assert!(
span.line >= 1 && span.column >= 1,
"input {input:?}: {path}"
);
assert!(
table.get_path(path).is_some(),
"input {input:?}: {path} is not in the document"
);
}
}
}
#[cfg(feature = "serde")]
#[test]
fn serde_round_trips_what_the_parser_accepts() {
let mut rng = Rng(0x5E12_DE00_1979_0527);
for _ in 0..20_000 {
let mut input = String::new();
for _ in 0..rng.below(24) {
input.push_str(rng.pick(PIECES));
}
let Ok(table) = parse(&input) else { continue };
let value = tomlproc::serde::to_value(&table).expect("a table always serializes");
let serialized = value.as_table().expect("a table serializes as a table");
assert_eq!(to_string(serialized), to_string(&table), "input {input:?}");
let back: tomlproc::Table =
tomlproc::serde::from_value(value).expect("a table deserializes");
assert_eq!(to_string(&back), to_string(&table), "input {input:?}");
let text = tomlproc::serde::to_string(&table).expect("a table serializes to a document");
assert_eq!(text, to_string(&table), "input {input:?}");
}
}