tatara_rust_proptest/
lib.rs1use proptest::prelude::*;
16use tatara_rust_ast::{Ident, RefKind, ToRustTokens, TypeRef};
17use tatara_rust_derive::{
18 PerFieldDeriveSpec, PerFieldTarget, PerVariantDeriveSpec, ProcDeriveSpec, VariantShape,
19};
20use tatara_rust_validate::Validate;
21
22pub fn arb_ident() -> impl Strategy<Value = Ident> {
29 "[A-Za-z_][A-Za-z0-9_]{0,15}".prop_map(Ident::new)
30}
31
32pub fn arb_ref_kind() -> impl Strategy<Value = RefKind> {
33 prop_oneof![
34 Just(RefKind::shared()),
35 Just(RefKind::mut_()),
36 Just(RefKind::shared_lifetime("a")),
37 Just(RefKind::shared_lifetime("static")),
38 ]
39}
40
41pub fn arb_type_ref() -> impl Strategy<Value = TypeRef> {
43 let leaf = arb_ident().prop_map(|i| TypeRef {
44 ident: i,
45 generics: vec![],
46 reference: None,
47 });
48 leaf.prop_recursive(3, 8, 3, |inner| {
49 (arb_ident(), prop::collection::vec(inner, 0..3), prop::option::of(arb_ref_kind())).prop_map(
50 |(ident, generics, reference)| TypeRef {
51 ident,
52 generics,
53 reference,
54 },
55 )
56 })
57}
58
59pub fn arb_per_field_spec() -> impl Strategy<Value = PerFieldDeriveSpec> {
66 (
67 arb_ident(),
68 Just("pub fn #field_name(&self) -> &#field_ty { &self.#field_name }".to_string()),
70 prop::option::of(Just("with_{}".to_string())),
71 prop::option::of(Just("MyTrait".to_string())),
72 )
73 .prop_map(
74 |(trait_name, per_field_template, method_name_template, trait_ref)| {
75 PerFieldDeriveSpec {
76 trait_name,
77 target: PerFieldTarget::NamedStruct,
78 trait_ref,
79 per_field_template,
80 method_name_template,
81 impl_prelude: None,
82 skip_fields: vec![],
83 field_attribute: None,
84 }
85 },
86 )
87}
88
89pub fn arb_per_variant_spec() -> impl Strategy<Value = PerVariantDeriveSpec> {
90 (
91 arb_ident(),
92 Just(
93 "pub fn #method_ident(&self) -> bool { matches!(self, #variant_shape_arm) }"
94 .to_string(),
95 ),
96 Just(Some("is_{}".to_string())),
97 prop::option::of(Just("MyTrait".to_string())),
98 )
99 .prop_map(
100 |(trait_name, per_variant_template, method_name_template, trait_ref)| {
101 PerVariantDeriveSpec {
102 trait_name,
103 variant_shape: VariantShape::Any,
104 trait_ref,
105 per_variant_template,
106 method_name_template,
107 impl_prelude: None,
108 }
109 },
110 )
111}
112
113pub fn arb_proc_derive_spec() -> impl Strategy<Value = ProcDeriveSpec> {
114 arb_ident().prop_map(|i| ProcDeriveSpec::new(i.0, vec![]))
115}
116
117pub fn prop_validate_is_total<T: Validate + Clone>(spec: &T) {
125 let v1 = spec.validate();
126 let v2 = spec.clone().validate();
127 assert_eq!(v1, v2, "Validate impl is non-deterministic");
128}
129
130pub fn prop_to_tokens_does_not_panic<T: ToRustTokens>(node: &T) {
133 let _ = node.to_rust_tokens();
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 proptest! {
141 #[test]
142 fn ident_is_always_nonempty(i in arb_ident()) {
143 prop_assert!(!i.0.is_empty());
144 }
145
146 #[test]
147 fn ident_to_tokens_does_not_panic(i in arb_ident()) {
148 prop_to_tokens_does_not_panic(&i);
149 }
150
151 #[test]
152 fn type_ref_to_tokens_does_not_panic(t in arb_type_ref()) {
153 prop_to_tokens_does_not_panic(&t);
154 }
155
156 #[test]
157 fn per_field_validate_is_total(s in arb_per_field_spec()) {
158 prop_validate_is_total(&s);
159 }
160
161 #[test]
162 fn per_variant_validate_is_total(s in arb_per_variant_spec()) {
163 prop_validate_is_total(&s);
164 }
165
166 #[test]
167 fn proc_derive_validate_is_total(s in arb_proc_derive_spec()) {
168 prop_validate_is_total(&s);
169 }
170 }
171}