use proptest::prelude::*;
use sui_spec::style::LabeledTable;
fn render_without_observing<F: FnOnce()>(f: F) {
f();
}
#[derive(Debug, Clone)]
enum Op {
Kv(String, String),
Opt(String, Option<String>),
Section(String, Option<usize>),
ListItem(String),
Blank,
}
fn arb_op() -> impl Strategy<Value = Op> {
prop_oneof![
("[a-zA-Z]{1,12}", "[a-zA-Z0-9 ]{0,40}").prop_map(|(k, v)| Op::Kv(k, v)),
("[a-zA-Z]{1,12}", proptest::option::of("[a-zA-Z0-9 ]{0,40}"))
.prop_map(|(k, v)| Op::Opt(k, v)),
("[a-zA-Z]{1,12}", proptest::option::of(0usize..100))
.prop_map(|(t, c)| Op::Section(t, c)),
"[a-zA-Z0-9]{1,20}".prop_map(Op::ListItem),
Just(Op::Blank),
]
}
proptest! {
#[test]
fn labeled_table_renders_arbitrary_ops_without_panic(
ops in proptest::collection::vec(arb_op(), 0..30),
label_w in 4usize..40,
) {
render_without_observing(|| {
let mut t = LabeledTable::new(label_w);
for op in &ops {
t = match op {
Op::Kv(k, v) => t.kv(k, v),
Op::Opt(k, v) => t.opt(k, v.as_deref()),
Op::Section(s, c) => t.section(s, *c),
Op::ListItem(it) => t.list_items("→", [it.as_str()]),
Op::Blank => t.blank(),
};
}
t.render();
});
prop_assert!(true);
}
#[test]
fn labeled_table_handles_extremes(
label_w in 0usize..200,
big in "[a-z]{200,400}",
small in "[a-z]{1,3}",
) {
render_without_observing(|| {
LabeledTable::new(label_w)
.kv(&big, &small)
.kv(&small, &big)
.opt(&big, Some(&small))
.opt(&small, None)
.section(&big, Some(99))
.list_items("→", [big.as_str(), small.as_str()])
.render();
});
prop_assert!(true);
}
#[test]
fn labeled_table_handles_unicode(
prefix in "[\u{4e00}-\u{9fff}]{1,5}", suffix in "[\u{1F300}-\u{1F5FF}]{0,3}", ) {
render_without_observing(|| {
LabeledTable::new(14)
.kv("label", &format!("{prefix}-{suffix}"))
.render();
});
prop_assert!(true);
}
#[test]
fn labeled_table_empty_section(seed in any::<u8>()) {
render_without_observing(|| {
LabeledTable::new(seed as usize % 30)
.section("", Some(0))
.render();
});
prop_assert!(true);
}
#[test]
fn labeled_table_can_be_built_repeatedly(
rows in proptest::collection::vec(("[a-z]{1,8}", "[a-z]{1,16}"), 1..10),
) {
let make = || {
let mut t = LabeledTable::new(14);
for (k, v) in &rows {
t = t.kv(k, v);
}
t
};
render_without_observing(|| {
make().render();
make().render();
});
prop_assert!(true);
}
}
proptest! {
#[test]
fn pick_format_returns_match_or_typed_error(
items in proptest::collection::vec("[a-z]{1,8}", 0..10),
target in "[a-z]{1,8}",
) {
use sui_spec::operator_view::pick_format;
let result = pick_format(items.clone(), |s| *s == target, "test");
match result {
Ok(found) => prop_assert!(items.contains(&found)),
Err(e) => {
let msg = format!("{e:?}");
prop_assert!(msg.contains("test"));
}
}
}
}