177 lines
5.1 KiB
Rust
177 lines
5.1 KiB
Rust
use ariadne::clew::{self, DictEntry, Error, TaggedValue, Value};
|
|
use serde_json::Number;
|
|
|
|
fn document_with(roots: &str) -> String {
|
|
format!(
|
|
r#"{{
|
|
"schema":"clew-renpy-ast-v1",
|
|
"source":"/tmp/game/script.rpyc",
|
|
"game_root":"/tmp/game",
|
|
"slot":2,
|
|
"python_version":"2.7.18",
|
|
"renpy_version":"8.4.0",
|
|
"metadata":null,
|
|
"roots":{roots}
|
|
}}"#
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn parses_minimal_document() {
|
|
let document = clew::from_bytes(document_with("null").as_bytes()).unwrap();
|
|
|
|
assert_eq!(document.schema, "clew-renpy-ast-v1");
|
|
assert_eq!(document.slot, 2);
|
|
assert_eq!(document.roots, Value::Null);
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_wrong_schema() {
|
|
let json = br#"{
|
|
"schema":"other",
|
|
"source":"/tmp/game/script.rpyc",
|
|
"game_root":"/tmp/game",
|
|
"slot":2,
|
|
"python_version":"2.7.18",
|
|
"renpy_version":null,
|
|
"metadata":null,
|
|
"roots":null
|
|
}"#;
|
|
|
|
assert!(matches!(
|
|
clew::from_bytes(json).unwrap_err(),
|
|
Error::SchemaMismatch {
|
|
expected: "clew-renpy-ast-v1",
|
|
actual
|
|
} if actual == "other"
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn parses_primitive_values() {
|
|
let document = clew::from_bytes(
|
|
document_with(r#"{"type":"list","items":[null,true,7,1.5,"text"]}"#).as_bytes(),
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
document.roots,
|
|
Value::Tagged(TaggedValue::List {
|
|
items: vec![
|
|
Value::Null,
|
|
Value::Bool(true),
|
|
Value::Number(Number::from(7)),
|
|
Value::Number(Number::from_f64(1.5).unwrap()),
|
|
Value::String("text".to_string()),
|
|
]
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parses_tuple_and_dict_values() {
|
|
let document = clew::from_bytes(
|
|
document_with(r#"{"type":"dict","items":[{"key":"names","value":{"type":"tuple","items":["a","b"]}}]}"#)
|
|
.as_bytes(),
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
document.roots,
|
|
Value::Tagged(TaggedValue::Dict {
|
|
items: vec![DictEntry {
|
|
key: Value::String("names".to_string()),
|
|
value: Value::Tagged(TaggedValue::Tuple {
|
|
items: vec![
|
|
Value::String("a".to_string()),
|
|
Value::String("b".to_string())
|
|
]
|
|
}),
|
|
}]
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parses_object_and_reference_values() {
|
|
let document = clew::from_bytes(
|
|
document_with(
|
|
r#"{"type":"list","items":[{"type":"object","id":1,"class":"renpy.ast.Label","attributes":{"name":"start","next":{"type":"ref","id":1}}},{"type":"ref","id":1}]}"#,
|
|
)
|
|
.as_bytes(),
|
|
)
|
|
.unwrap();
|
|
|
|
let Value::Tagged(TaggedValue::List { items }) = document.roots else {
|
|
panic!("roots should be a list");
|
|
};
|
|
assert_eq!(items.len(), 2);
|
|
assert_eq!(items[1], Value::Tagged(TaggedValue::Ref { id: 1 }));
|
|
let Value::Tagged(TaggedValue::Object {
|
|
id,
|
|
class_name,
|
|
attributes,
|
|
}) = &items[0]
|
|
else {
|
|
panic!("first item should be an object");
|
|
};
|
|
assert_eq!(*id, 1);
|
|
assert_eq!(class_name, "renpy.ast.Label");
|
|
assert_eq!(attributes["name"], Value::String("start".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn parses_special_tag_values() {
|
|
let document = clew::from_bytes(
|
|
document_with(
|
|
r#"{"type":"list","items":[{"type":"function","name":"f","module":"m","filename":"file.py","firstlineno":9},{"type":"method","name":"g","module":"m","receiver_class":"C"},{"type":"python-type","name":"Label","module":"renpy.ast"},{"type":"repr","class":"C","repr":"<C>"},{"type":"bytes","repr":"b'abc'"},{"type":"truncated","class":"C","repr":"<C>"},{"type":"recursion","class":"list"},{"type":"attribute-error","class":"Exception","repr":"Exception('x')"}]}"#,
|
|
)
|
|
.as_bytes(),
|
|
)
|
|
.unwrap();
|
|
|
|
let Value::Tagged(TaggedValue::List { items }) = document.roots else {
|
|
panic!("roots should be a list");
|
|
};
|
|
assert_eq!(items.len(), 8);
|
|
assert!(matches!(
|
|
items[0],
|
|
Value::Tagged(TaggedValue::Function(_))
|
|
));
|
|
assert!(matches!(items[1], Value::Tagged(TaggedValue::Method(_))));
|
|
assert!(matches!(
|
|
items[2],
|
|
Value::Tagged(TaggedValue::PythonType(_))
|
|
));
|
|
assert!(matches!(
|
|
items[3],
|
|
Value::Tagged(TaggedValue::Repr { .. })
|
|
));
|
|
assert!(matches!(
|
|
items[4],
|
|
Value::Tagged(TaggedValue::Bytes { .. })
|
|
));
|
|
assert!(matches!(
|
|
items[5],
|
|
Value::Tagged(TaggedValue::Truncated { .. })
|
|
));
|
|
assert!(matches!(
|
|
items[6],
|
|
Value::Tagged(TaggedValue::Recursion { .. })
|
|
));
|
|
assert!(matches!(
|
|
items[7],
|
|
Value::Tagged(TaggedValue::AttributeError { .. })
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_unknown_tags() {
|
|
let error = clew::from_bytes(document_with(r#"{"type":"future"}"#).as_bytes()).unwrap_err();
|
|
|
|
match error {
|
|
Error::Json(error) => assert!(error.to_string().contains("data did not match any variant")),
|
|
Error::SchemaMismatch { .. } => panic!("unknown tags should be JSON shape errors"),
|
|
}
|
|
}
|