focus/crates/focus-json/tests/parser_tests.rs

156 lines
4.4 KiB
Rust

//! parser 模块的集成测试。
//! Integration tests for the parser module.
use focus_json::{parse, JsonError, JsonValue};
fn ok(input: &str) -> JsonValue {
parse(input).unwrap_or_else(|e| panic!("failed to parse {:?}: {}", input, e))
}
fn err(input: &str) -> JsonError {
parse(input).expect_err(&format!("expected parse error for {:?}", input))
}
// ---- literals ---------------------------------------------------------
#[test]
fn parses_literals() {
assert_eq!(ok("true"), JsonValue::Bool(true));
assert_eq!(ok("false"), JsonValue::Bool(false));
assert_eq!(ok("null"), JsonValue::Null);
}
#[test]
fn rejects_truncated_keywords() {
assert!(err("tru").message.contains("expected 'true'"));
assert!(err("nul").message.contains("expected 'null'"));
}
// ---- numbers ----------------------------------------------------------
#[test]
fn parses_numbers() {
assert_eq!(ok("0"), JsonValue::Num(0.0));
assert_eq!(ok("-0"), JsonValue::Num(0.0));
assert_eq!(ok("42"), JsonValue::Num(42.0));
assert_eq!(ok("-42"), JsonValue::Num(-42.0));
assert_eq!(ok("12.345"), JsonValue::Num(12.345));
assert_eq!(ok("1e10"), JsonValue::Num(1e10));
assert_eq!(ok("1.5e-3"), JsonValue::Num(1.5e-3));
assert_eq!(ok("2.5E+4"), JsonValue::Num(2.5e4));
}
#[test]
fn rejects_leading_zeros() {
assert!(err("01").message.contains("leading zero"));
assert!(err("00").message.contains("leading zero"));
}
#[test]
fn rejects_trailing_dot() {
assert!(err("3.").message.contains("decimal"));
}
#[test]
fn rejects_empty_exponent() {
assert!(err("1e").message.contains("exponent"));
}
// ---- strings ----------------------------------------------------------
#[test]
fn parses_simple_strings() {
assert_eq!(ok(r#""hello""#), JsonValue::Str("hello".into()));
assert_eq!(ok(r#""""#), JsonValue::Str("".into()));
}
#[test]
fn parses_string_escapes() {
assert_eq!(ok(r#""a\"b""#), JsonValue::Str("a\"b".into()));
assert_eq!(ok(r#""\n\t\r""#), JsonValue::Str("\n\t\r".into()));
assert_eq!(ok(r#""\\""#), JsonValue::Str("\\".into()));
assert_eq!(ok(r#""\/""#), JsonValue::Str("/".into()));
assert_eq!(ok(r#""\b\f""#), JsonValue::Str("\u{0008}\u{000C}".into()));
}
#[test]
fn parses_unicode_escapes() {
assert_eq!(ok(r#""\u0041""#), JsonValue::Str("A".into()));
assert_eq!(ok(r#""\u4e2d""#), JsonValue::Str("".into()));
// U+1F600 😀 的代理对。
// Surrogate pair for U+1F600 😀
assert_eq!(ok(r#""\uD83D\uDE00""#), JsonValue::Str("😀".into()));
}
#[test]
fn rejects_lone_surrogates() {
assert!(err(r#""\uD800""#).message.contains("low surrogate"));
assert!(err(r#""\uDC00""#).message.contains("low surrogate"));
}
#[test]
fn rejects_unterminated_string() {
assert!(err(r#""abc"#).message.contains("unterminated"));
}
#[test]
fn rejects_unescaped_control_chars() {
assert!(err("\"a\nb\"").message.contains("control character"));
}
#[test]
fn handles_utf8_multibyte() {
assert_eq!(ok("\"中文\""), JsonValue::Str("中文".into()));
assert_eq!(ok("\"😀\""), JsonValue::Str("😀".into()));
}
// ---- objects / arrays -------------------------------------------------
#[test]
fn parses_empty_containers() {
assert_eq!(ok("{}"), JsonValue::obj());
assert_eq!(ok("[]"), JsonValue::arr());
}
#[test]
fn parses_nested() {
let v = ok(r#"{"a":[1,2,{"b":true}],"c":null}"#);
assert_eq!(v.get_arr("a").unwrap()[2].get_bool("b"), Some(true));
assert!(v.get("c").unwrap().is_null());
}
#[test]
fn handles_whitespace() {
let v = ok(" { \"x\" : 1 } ");
assert_eq!(v.get_num("x"), Some(1.0));
}
// ---- structural errors ------------------------------------------------
#[test]
fn rejects_trailing_comma() {
// 尾随逗号会被拒绝,因为下一个字符(']' 或 '}')不是合法的值/成员起始。
// A trailing comma is rejected because the next char (']' or '}') is
// not a valid value/member start.
assert!(parse("[1,2,]").is_err());
assert!(parse("{\"a\":1,}").is_err());
}
#[test]
fn rejects_trailing_characters() {
let e = err("1 2");
assert!(e.message.contains("trailing"));
}
#[test]
fn rejects_empty_input() {
assert!(err("").message.contains("end of input"));
assert!(err(" ").message.contains("end of input"));
}
#[test]
fn rejects_unclosed_containers() {
assert!(err("[1,2").message.contains("end of input"));
assert!(err("{\"a\":1").message.contains("end of input"));
}