use super::super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn parse_doesnt_panic_on_arbitrary_bytes(input in prop::collection::vec(any::<u8>(), 0..1024)) {
let _ = try_parse(&input); }
#[test]
fn parse_doesnt_panic_on_arbitrary_strings(input in ".*") {
let _ = try_parse(input.as_bytes()); }
#[test]
fn parse_rejects_deep_nesting_gracefully(depth in 1usize..100) {
let mut json = String::new();
for _ in 0..depth {
json.push_str("{\"a\":");
}
json.push('1');
for _ in 0..depth {
json.push('}');
}
let _ = try_parse(json.as_bytes());
}
#[test]
fn parse_rejects_deep_array_nesting_gracefully(depth in 1usize..100) {
let mut json = String::new();
for _ in 0..depth {
json.push('[');
}
json.push('1');
for _ in 0..depth {
json.push(']');
}
let _ = try_parse(json.as_bytes());
}
#[test]
fn parse_handles_unicode_strings(s in "\\PC*") {
let json = format!(r#"{{"text": "{}"}}"#, s.replace('\\', "\\\\").replace('"', "\\\""));
let _ = try_parse(json.as_bytes()); }
#[test]
fn parse_handles_valid_utf8(s in "[a-zA-Z0-9 ]{0,100}") {
let json = format!(r#"{{"value": "{s}"}}"#);
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
let value = result.unwrap();
prop_assert_eq!(value.path_str(&["value"]), Some(s));
}
#[test]
fn parse_handles_large_integers(n in i64::MIN..=i64::MAX) {
let json = format!(r#"{{"n": {n}}}"#);
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_large_unsigned(n in 0u64..=u64::MAX) {
let json = format!(r#"{{"n": {n}}}"#);
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_floats(f in any::<f64>().prop_filter("must be finite", |x| x.is_finite())) {
let json = format!(r#"{{"n": {f}}}"#);
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_nan_like_strings(s in prop::sample::select(vec![
"NaN", "nan", "NAN", "Infinity", "-Infinity", "inf", "-inf"
])) {
let json_raw = format!(r#"{{"n": {s}}}"#);
let _ = try_parse(json_raw.as_bytes());
let json_str = format!(r#"{{"n": "{s}"}}"#);
let result = try_parse(json_str.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_scientific_notation(
mantissa in -1000i64..1000i64,
exponent in -308i32..308i32
) {
let json = format!(r#"{{"n": {mantissa}e{exponent}}}"#);
let _ = try_parse(json.as_bytes()); }
#[test]
fn parse_handles_long_strings(len in 0usize..10000) {
let long_string = "x".repeat(len);
let json = format!(r#"{{"s": "{long_string}"}}"#);
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_large_arrays(len in 0usize..1000) {
let elements: Vec<String> = (0..len).map(|i| i.to_string()).collect();
let json = format!("[{}]", elements.join(","));
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_large_objects(len in 0usize..500) {
let entries: Vec<String> = (0..len).map(|i| format!(r#""k{i}": {i}"#)).collect();
let json = format!("{{{}}}", entries.join(","));
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn depth_check_doesnt_panic(input in prop::collection::vec(any::<u8>(), 0..2048)) {
let _ = json_depth_exceeds_limit(&input); }
#[test]
fn depth_check_ignores_braces_in_strings(
prefix in "[a-z]{0,10}",
braces in "[\\{\\}\\[\\]]{0,50}",
suffix in "[a-z]{0,10}"
) {
let json = format!(r#"{{"key": "{prefix}{braces}{suffix}"}}"#);
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_escape_sequences(s in prop::sample::select(vec![
r#"\""#, r"\\", r"\/", r"\b", r"\f", r"\n", r"\r", r"\t"
])) {
let json = format!(r#"{{"s": "{s}"}}"#);
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
#[test]
fn parse_handles_unicode_escapes(code in 0u16..0xFFFF) {
if !(0xD800..=0xDFFF).contains(&code) {
let json = format!(r#"{{"s": "\\u{code:04X}"}}"#);
let _ = try_parse(json.as_bytes()); }
}
#[test]
fn parse_handles_mixed_nesting(depth in 1usize..20) {
let mut json = String::new();
for i in 0..depth {
if i % 2 == 0 {
json.push_str("{\"a\":");
} else {
json.push('[');
}
}
json.push('1');
for i in (0..depth).rev() {
if i % 2 == 0 {
json.push('}');
} else {
json.push(']');
}
}
let result = try_parse(json.as_bytes());
prop_assert!(result.is_some());
}
}
proptest! {
#[test]
fn string_roundtrip(s in ".*") {
let json = s.to_json();
let output = json.to_string();
prop_assert!(output.starts_with('"'));
prop_assert!(output.ends_with('"'));
}
#[test]
fn i32_roundtrip(n in any::<i32>()) {
let json = n.to_json();
let output = json.to_string();
let parsed: i64 = output.parse().unwrap();
prop_assert_eq!(parsed, i64::from(n));
}
#[test]
fn i64_roundtrip(n in any::<i64>()) {
let json = n.to_json();
let output = json.to_string();
let parsed: i64 = output.parse().unwrap();
prop_assert_eq!(parsed, n);
}
#[test]
fn bool_roundtrip(b in any::<bool>()) {
let json = b.to_json();
let output = json.to_string();
prop_assert_eq!(output, if b { "true" } else { "false" });
}
#[test]
fn option_none_is_null(opt in Just(None::<i32>)) {
let json = opt.to_json();
prop_assert_eq!(json.to_string(), "null");
}
#[test]
fn vec_length_preserved(v in prop::collection::vec(any::<i32>(), 0..100)) {
let json = v.to_json();
let len = json.len();
prop_assert_eq!(len, Some(v.len()));
}
}