use sml::parse;
#[test]
fn p0_1_unclosed_contract_must_error() {
let r = parse("@contract C { a: str\nreal_key: 1");
assert!(r.is_err(), "契约体未闭合应报错,实际 ok: {:?}", r);
let r = parse("@contract C { a: str\nk1: 1\nk2: 2\nk3: 3");
assert!(r.is_err(), "契约体未闭合(多行)应报错,实际 ok: {:?}", r);
}
#[test]
fn p0_1_well_formed_contract_ok() {
let r = parse("@contract C { a: str }");
assert!(r.is_ok(), "合法 @contract 应 ok,实际 err: {:?}", r);
}
#[test]
fn p0_2_lone_at_must_error() {
let r = parse("@\nk: 1");
assert!(r.is_err(), "单独 @ 应报错,实际 ok: {:?}", r);
let r = parse("o {\n@\nk: 1\n}");
assert!(r.is_err(), "块内单独 @ 应报错,实际 ok: {:?}", r);
let r = parse("@notacommand");
assert!(r.is_err(), "非指令 @name 无片段体应报错,实际 ok: {:?}", r);
}
#[test]
fn p0_2_valid_fragment_still_ok() {
let r = parse("@base { x: 1 }\nuse: &base");
assert!(r.is_ok(), "合法片段定义应 ok,实际 err: {:?}", r);
let r = parse("@blk { x: 1 }");
assert!(r.is_ok(), "合法片段语法 @blk {{}} 应 ok,实际 err: {:?}", r);
}
#[test]
fn p1_3_bare_block_keeps_all_args() {
let r = parse("server web prod { port: 80 host: h }");
assert!(r.is_ok(), "裸块应 ok,实际 err: {:?}", r);
let v = r.unwrap();
if let sml::Value::Object(m) = &v {
let server = m.get("server").unwrap();
if let sml::Value::Object(sm) = server {
assert_eq!(sm.get("__type"), Some(&sml::Value::Str("server".into())));
assert_eq!(sm.get("__name"), Some(&sml::Value::Str("web".into())));
match sm.get("__args") {
Some(sml::Value::Array(a)) => {
assert!(a.iter().any(|x| x == &sml::Value::Str("prod".into())));
}
other => panic!("prod 应进入 __args 数组,实际 {:?}", other),
}
} else {
panic!("server 应为对象,实际 {:?}", server);
}
} else {
panic!("顶层应为对象,实际 {:?}", v);
}
}
#[test]
fn p1_4_leading_zero_kept_as_str() {
let r = parse("mode: 0755");
assert_eq!(r.unwrap().get("mode"), Some(&sml::Value::Str("0755".into())));
let r = parse("id: 007");
assert_eq!(r.unwrap().get("id"), Some(&sml::Value::Str("007".into())));
let r = parse("x: 0xFF");
assert_eq!(r.unwrap().get("x"), Some(&sml::Value::Str("0xFF".into())));
let r = parse("n: 123");
assert_eq!(r.unwrap().get("n"), Some(&sml::Value::Int(123)));
}
#[test]
fn p1_5_unknown_escape_must_error() {
let r = parse("p: \"C:\\Users\"");
assert!(r.is_err(), "未知转义 \\U 应报错,实际 ok: {:?}", r);
let r = parse("re: \"\\d+\"");
assert!(r.is_err(), "未知转义 \\d 应报错,实际 ok: {:?}", r);
let r = parse("x: \"\\z\"");
assert!(r.is_err(), "未知转义 \\z 应报错,实际 ok: {:?}", r);
let r = parse("p: \"a\\nb\"");
assert_eq!(r.unwrap().get("p"), Some(&sml::Value::Str("a\nb".into())));
}
#[test]
fn p2_7_comma_in_bareword_must_error() {
let r = parse("a: x,y");
assert!(r.is_err(), "裸词含逗号应报错,实际 ok: {:?}", r);
let r = parse("a: [x, y]");
assert!(r.is_ok(), "数组语法应 ok,实际 err: {:?}", r);
let r = parse("a: \"x,y\"");
assert_eq!(r.unwrap().get("a"), Some(&sml::Value::Str("x,y".into())));
}
#[test]
fn p2_6_comment_no_leading_space_still_ok() {
let r = parse("a: 1#c");
assert_eq!(r.unwrap().get("a"), Some(&sml::Value::Int(1)));
let r = parse("a: x#c");
assert_eq!(r.unwrap().get("a"), Some(&sml::Value::Str("x".into())));
let r = parse("port: 465# 注释");
assert_eq!(r.unwrap().get("port"), Some(&sml::Value::Int(465)));
}