use plushie::runtime_internals::{diff_tree, try_apply_patch};
use plushie_core::protocol::{Props, TreeNode};
use proptest::prelude::*;
use serde_json::{Value, json};
fn arb_leaf_value() -> impl Strategy<Value = Value> {
prop_oneof![
Just(Value::Null),
any::<bool>().prop_map(Value::Bool),
any::<i32>().prop_map(|n| json!(n)),
"[a-z0-9_]{0,8}".prop_map(Value::String),
]
}
fn arb_props() -> impl Strategy<Value = Props> {
prop::collection::vec(("[a-z]{1,6}", arb_leaf_value()), 0..4).prop_map(|pairs| {
let map: serde_json::Map<String, Value> = pairs.into_iter().collect();
Props::from_json(Value::Object(map))
})
}
const LEAF_TYPE_NAMES: &[&str] = &[
"text",
"button",
"spacer",
"image",
"checkbox",
"toggler",
"rule",
"qr_code",
"progress_bar",
"svg",
];
const CONTAINER_TYPE_NAMES: &[&str] = &[
"column",
"row",
"container",
"stack",
"scrollable",
"grid",
"tooltip",
"themed",
"layer",
];
fn arb_tree() -> impl Strategy<Value = TreeNode> {
let leaf = (
"[a-z][a-z0-9_]{0,4}",
prop::sample::select(LEAF_TYPE_NAMES),
arb_props(),
)
.prop_map(|(id, type_name, props)| TreeNode {
id,
type_name: type_name.to_string(),
props,
children: vec![],
});
leaf.prop_recursive(
3, 16, 4, |inner| {
(
"[a-z][a-z0-9_]{0,4}",
prop::sample::select(CONTAINER_TYPE_NAMES),
arb_props(),
prop::collection::vec(inner, 0..4),
)
.prop_map(|(id, type_name, props, children)| TreeNode {
id,
type_name: type_name.to_string(),
props,
children,
})
},
)
}
proptest! {
#[test]
fn apply_diff_round_trips_on_arbitrary_tree_pairs(
a in arb_tree(),
b in arb_tree(),
) {
let ops = diff_tree(&a, &b);
let mut a_copy = a.clone();
let result = try_apply_patch(&mut a_copy, &ops);
prop_assert!(result.is_ok(), "patch apply failed: {:?}", result.err());
prop_assert_eq!(&a_copy, &b);
}
#[test]
fn apply_diff_identity_when_trees_are_equal(t in arb_tree()) {
let ops = diff_tree(&t, &t);
prop_assert!(
ops.is_empty(),
"diff of a tree against itself must be empty; got {} ops",
ops.len()
);
let mut copy = t.clone();
let result = try_apply_patch(&mut copy, &ops);
prop_assert!(result.is_ok(), "patch apply failed: {:?}", result.err());
prop_assert_eq!(copy, t);
}
}