From f3789322a59b3b7ae154f806838f81c1e585cc98 Mon Sep 17 00:00:00 2001 From: Matteo Almanza Date: Fri, 16 Aug 2024 18:07:39 +0200 Subject: [PATCH 1/3] test: proptest for define functions --- clar2wasm/tests/wasm-generation/functions.rs | 333 +++++++++++++++++++ clar2wasm/tests/wasm-generation/main.rs | 1 + 2 files changed, 334 insertions(+) create mode 100644 clar2wasm/tests/wasm-generation/functions.rs diff --git a/clar2wasm/tests/wasm-generation/functions.rs b/clar2wasm/tests/wasm-generation/functions.rs new file mode 100644 index 00000000..9e7afc8a --- /dev/null +++ b/clar2wasm/tests/wasm-generation/functions.rs @@ -0,0 +1,333 @@ +use clar2wasm::tools::crosscheck; +use clarity::vm::{ + types::{ResponseData, TupleData, TypeSignature}, + ClarityName, Value, +}; +use proptest::prelude::*; +use proptest::proptest; +use proptest::strategy::Strategy; + +use crate::{prop_signature, response, type_string, PropValue}; + +fn strategies_for_function_siganture() -> impl Strategy, Vec)> +{ + prop::collection::vec( + prop_signature().prop_ind_flat_map2(|ty| PropValue::from_type(ty.clone())), + 1..=20, + ) + .prop_map(|arg_ty| arg_ty.into_iter().unzip::<_, _, Vec<_>, Vec<_>>()) + .no_shrink() +} + +fn strategies_for_response() -> impl Strategy { + (prop_signature(), prop_signature()) + .prop_flat_map(|(ok, err)| response(ok, err)) + .prop_map(PropValue::from) + .no_shrink() +} + +/// Given a list of type signatures, join them together with some generated arguments names +/// and return both the formatted signature `(arg-0 type-0) ...` and the list of +/// generated arguments names +fn format_args_signature(tys: &Vec) -> (String, Vec) { + let args: Vec = (0..tys.len()).map(|i| format!("arg-{i}")).collect(); + let sig = args + .iter() + .zip(tys) + .map(|(arg, ty)| format!("({arg} {})", type_string(ty))) + .collect::>() + .join(" "); + (sig, args) +} + +fn join_stringified(values: &[PropValue]) -> String { + values + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(" ") +} + +proptest! { + #![proptest_config(super::runtime_config())] + + #[test] + fn crossprop_define_private_accepts_any_args( + (tys, values) in strategies_for_function_siganture(), + result in PropValue::any().no_shrink() + ) { + let (args_signature, _) = format_args_signature(&tys); + let call_args = join_stringified(&values); + crosscheck( + &format!( + r#" + (define-private (some-fn {args_signature}) + {result} + ) + (some-fn {call_args}) + "#, + ), + Ok(Some(result.into())), + ) + } + + #[test] + fn crossprop_define_private_returns_any_argument( + ((tys, values), return_index) in strategies_for_function_siganture().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), + ) { + let (args_signature, args_name) = format_args_signature(&tys); + let call_args = join_stringified(&values); + crosscheck( + &format!( + r#" + (define-private (some-fn {args_signature}) + {} + ) + (some-fn {call_args}) + "#, args_name[return_index] + ), + Ok(Some(values[return_index].clone().into())), + ) + } + + #[test] + fn crossprop_define_private_can_use_all_arguments((tys, values) in strategies_for_function_siganture()) { + let (args_signature, args_name) = format_args_signature(&tys); + let call_args = join_stringified(&values); + + let return_exp = args_name.iter().map(|arg| format!("ret-{arg}: {arg}")).collect::>().join(", "); + let expected = TupleData::from_data( + args_name.iter() + .map(|arg| ClarityName::try_from(format!("ret-{arg}")).unwrap()) + .zip(values.into_iter().map(Value::from)) + .collect(), + ) + .unwrap() + .into(); + + crosscheck( + &format!( + r#" + (define-private (some-fn {args_signature}) + {{ {return_exp} }} + ) + (some-fn {call_args}) + "#, + ), + Ok(Some(expected)), + ) + } + + #[test] + fn crossprop_define_private_side_effects( + (tys, values) in strategies_for_function_siganture(), + response in strategies_for_response()) + { + let (args_signature, _) = format_args_signature(&tys); + let call_args = join_stringified(&values); + + let expected = TupleData::from_data(vec![ + (ClarityName::from("fn"), response.clone().into()), + // Response does not affect private functions + (ClarityName::from("side"), Value::Bool(true)), + ]).unwrap().into(); + + crosscheck( + &format!( + r#" + (define-data-var side bool false) + (define-private (some-fn {args_signature}) + (begin + (var-set side true) + {response} + ) + ) + {{ fn: (some-fn {call_args}), side: (var-get side) }} + "#, + ), + Ok(Some(expected)), + ) + } + + + #[test] + fn crossprop_define_public_accepts_any_args( + (tys, values) in strategies_for_function_siganture(), + response in strategies_for_response()) + { + let (args_signature, _) = format_args_signature(&tys); + let call_args = join_stringified(&values); + crosscheck( + &format!( + r#" + (define-public (some-fn {args_signature}) + {response} + ) + (some-fn {call_args}) + "#, + ), + Ok(Some(response.into())), + ) + } + + #[test] + fn crossprop_define_public_returns_any_argument( + ((tys, values), return_index) in strategies_for_function_siganture().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), + response_ok in any::() + ) { + let (args_signature, args_name) = format_args_signature(&tys); + let call_args = join_stringified(&values); + let expected = Value::Response(ResponseData { + committed: response_ok, + data: Box::new(values[return_index].clone().into()), + }); + crosscheck( + &format!( + r#" + (define-public (some-fn {args_signature}) + ({} {}) + ) + (some-fn {call_args}) + "#, if response_ok { "ok" } else { "err" }, args_name[return_index] + ), + Ok(Some(expected)), + ) + } + + #[test] + fn crossprop_define_public_can_use_all_arguments( + (tys, values) in strategies_for_function_siganture(), + response_ok in any::() + ) { + let (args_signature, args_name) = format_args_signature(&tys); + let call_args = join_stringified(&values); + + let return_exp = args_name.iter().map(|arg| format!("ret-{arg}: {arg}")).collect::>().join(", "); + let expected = TupleData::from_data( + args_name.iter() + .map(|arg| ClarityName::try_from(format!("ret-{arg}")).unwrap()) + .zip(values.into_iter().map(Value::from)) + .collect(), + ).unwrap(); + let expected = Value::Response(ResponseData { + committed: response_ok, + data: Box::new(expected.into()), + }); + + crosscheck( + &format!( + r#" + (define-public (some-fn {args_signature}) + ({} {{ {return_exp} }}) + ) + (some-fn {call_args}) + "#, if response_ok { "ok" } else { "err" } + ), + Ok(Some(expected)), + ) + } + + #[test] + fn crossprop_define_public_side_effects( + (tys, values) in strategies_for_function_siganture(), + response in strategies_for_response()) + { + let (args_signature, _) = format_args_signature(&tys); + let call_args = join_stringified(&values); + + let expected = TupleData::from_data(vec![ + (ClarityName::from("fn"), response.clone().into()), + // Err responses revert changes (`(var-set side true)`) + (ClarityName::from("side"), Value::Bool( + match response { + PropValue(Value::Response(ResponseData{ committed, ..})) => committed, + _ => unreachable!("Expected a response") + } + )), + ]).unwrap().into(); + + crosscheck( + &format!( + r#" + (define-data-var side bool false) + (define-public (some-fn {args_signature}) + (begin + (var-set side true) + {response} + ) + ) + {{ fn: (some-fn {call_args}), side: (var-get side) }} + "#, + ), + Ok(Some(expected)), + ) + } + + + #[test] + fn crossprop_define_readonly_accepts_any_args( + (tys, values) in strategies_for_function_siganture(), + result in PropValue::any().no_shrink() + ) { + let (args_signature, _) = format_args_signature(&tys); + let call_args = join_stringified(&values); + crosscheck( + &format!( + r#" + (define-read-only (some-fn {args_signature}) + {result} + ) + (some-fn {call_args}) + "#, + ), + Ok(Some(result.into())), + ) + } + + #[test] + fn crossprop_define_readonly_returns_any_argument( + ((tys, values), return_index) in strategies_for_function_siganture().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), + ) { + let (args_signature, args_name) = format_args_signature(&tys); + let call_args = join_stringified(&values); + crosscheck( + &format!( + r#" + (define-read-only (some-fn {args_signature}) + {} + ) + (some-fn {call_args}) + "#, args_name[return_index] + ), + Ok(Some(values[return_index].clone().into())), + ) + } + + #[test] + fn crossprop_define_readonly_can_use_all_arguments((tys, values) in strategies_for_function_siganture()) { + let (args_signature, args_name) = format_args_signature(&tys); + let call_args = join_stringified(&values); + + let return_exp = args_name.iter().map(|arg| format!("ret-{arg}: {arg}")).collect::>().join(", "); + let expected = TupleData::from_data( + args_name.iter() + .map(|arg| ClarityName::try_from(format!("ret-{arg}")).unwrap()) + .zip(values.into_iter().map(Value::from)) + .collect(), + ) + .unwrap() + .into(); + + crosscheck( + &format!( + r#" + (define-read-only (some-fn {args_signature}) + {{ {return_exp} }} + ) + (some-fn {call_args}) + "#, + ), + Ok(Some(expected)), + ) + } +} diff --git a/clar2wasm/tests/wasm-generation/main.rs b/clar2wasm/tests/wasm-generation/main.rs index d012e908..682382cf 100644 --- a/clar2wasm/tests/wasm-generation/main.rs +++ b/clar2wasm/tests/wasm-generation/main.rs @@ -10,6 +10,7 @@ pub mod contracts; pub mod control_flow; pub mod default_to; pub mod equal; +pub mod functions; pub mod hashing; pub mod maps; pub mod noop; From e7129599c717c0d0d7d0089d8e31eaf669e0c486 Mon Sep 17 00:00:00 2001 From: Matteo Almanza Date: Fri, 16 Aug 2024 18:24:13 +0200 Subject: [PATCH 2/3] chore: formatting --- clar2wasm/tests/wasm-generation/functions.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clar2wasm/tests/wasm-generation/functions.rs b/clar2wasm/tests/wasm-generation/functions.rs index 9e7afc8a..208c4dfe 100644 --- a/clar2wasm/tests/wasm-generation/functions.rs +++ b/clar2wasm/tests/wasm-generation/functions.rs @@ -1,8 +1,6 @@ use clar2wasm::tools::crosscheck; -use clarity::vm::{ - types::{ResponseData, TupleData, TypeSignature}, - ClarityName, Value, -}; +use clarity::vm::types::{ResponseData, TupleData, TypeSignature}; +use clarity::vm::{ClarityName, Value}; use proptest::prelude::*; use proptest::proptest; use proptest::strategy::Strategy; From 3c00cae3b8e4a0aa46131d0b807618aa07247d9b Mon Sep 17 00:00:00 2001 From: Matteo Almanza Date: Mon, 19 Aug 2024 12:33:55 +0200 Subject: [PATCH 3/3] chore: typo --- clar2wasm/tests/wasm-generation/functions.rs | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/clar2wasm/tests/wasm-generation/functions.rs b/clar2wasm/tests/wasm-generation/functions.rs index 208c4dfe..0be0e0b0 100644 --- a/clar2wasm/tests/wasm-generation/functions.rs +++ b/clar2wasm/tests/wasm-generation/functions.rs @@ -7,7 +7,7 @@ use proptest::strategy::Strategy; use crate::{prop_signature, response, type_string, PropValue}; -fn strategies_for_function_siganture() -> impl Strategy, Vec)> +fn strategies_for_function_signature() -> impl Strategy, Vec)> { prop::collection::vec( prop_signature().prop_ind_flat_map2(|ty| PropValue::from_type(ty.clone())), @@ -51,7 +51,7 @@ proptest! { #[test] fn crossprop_define_private_accepts_any_args( - (tys, values) in strategies_for_function_siganture(), + (tys, values) in strategies_for_function_signature(), result in PropValue::any().no_shrink() ) { let (args_signature, _) = format_args_signature(&tys); @@ -71,7 +71,7 @@ proptest! { #[test] fn crossprop_define_private_returns_any_argument( - ((tys, values), return_index) in strategies_for_function_siganture().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), + ((tys, values), return_index) in strategies_for_function_signature().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), ) { let (args_signature, args_name) = format_args_signature(&tys); let call_args = join_stringified(&values); @@ -89,7 +89,7 @@ proptest! { } #[test] - fn crossprop_define_private_can_use_all_arguments((tys, values) in strategies_for_function_siganture()) { + fn crossprop_define_private_can_use_all_arguments((tys, values) in strategies_for_function_signature()) { let (args_signature, args_name) = format_args_signature(&tys); let call_args = join_stringified(&values); @@ -118,7 +118,7 @@ proptest! { #[test] fn crossprop_define_private_side_effects( - (tys, values) in strategies_for_function_siganture(), + (tys, values) in strategies_for_function_signature(), response in strategies_for_response()) { let (args_signature, _) = format_args_signature(&tys); @@ -150,7 +150,7 @@ proptest! { #[test] fn crossprop_define_public_accepts_any_args( - (tys, values) in strategies_for_function_siganture(), + (tys, values) in strategies_for_function_signature(), response in strategies_for_response()) { let (args_signature, _) = format_args_signature(&tys); @@ -170,7 +170,7 @@ proptest! { #[test] fn crossprop_define_public_returns_any_argument( - ((tys, values), return_index) in strategies_for_function_siganture().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), + ((tys, values), return_index) in strategies_for_function_signature().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), response_ok in any::() ) { let (args_signature, args_name) = format_args_signature(&tys); @@ -194,7 +194,7 @@ proptest! { #[test] fn crossprop_define_public_can_use_all_arguments( - (tys, values) in strategies_for_function_siganture(), + (tys, values) in strategies_for_function_signature(), response_ok in any::() ) { let (args_signature, args_name) = format_args_signature(&tys); @@ -227,7 +227,7 @@ proptest! { #[test] fn crossprop_define_public_side_effects( - (tys, values) in strategies_for_function_siganture(), + (tys, values) in strategies_for_function_signature(), response in strategies_for_response()) { let (args_signature, _) = format_args_signature(&tys); @@ -264,7 +264,7 @@ proptest! { #[test] fn crossprop_define_readonly_accepts_any_args( - (tys, values) in strategies_for_function_siganture(), + (tys, values) in strategies_for_function_signature(), result in PropValue::any().no_shrink() ) { let (args_signature, _) = format_args_signature(&tys); @@ -284,7 +284,7 @@ proptest! { #[test] fn crossprop_define_readonly_returns_any_argument( - ((tys, values), return_index) in strategies_for_function_siganture().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), + ((tys, values), return_index) in strategies_for_function_signature().prop_ind_flat_map2(|(tys, _)| 0..tys.len()), ) { let (args_signature, args_name) = format_args_signature(&tys); let call_args = join_stringified(&values); @@ -302,7 +302,7 @@ proptest! { } #[test] - fn crossprop_define_readonly_can_use_all_arguments((tys, values) in strategies_for_function_siganture()) { + fn crossprop_define_readonly_can_use_all_arguments((tys, values) in strategies_for_function_signature()) { let (args_signature, args_name) = format_args_signature(&tys); let call_args = join_stringified(&values);