156 lines
4.4 KiB
Rust
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"));
|
|
}
|