Skip to content

Commit

Permalink
Improve readability and fix some spacing
Browse files Browse the repository at this point in the history
  • Loading branch information
bugarela committed May 24, 2024
1 parent 3aec37b commit d6990e6
Show file tree
Hide file tree
Showing 14 changed files with 149 additions and 182 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ cargo clean && cargo cosmwasm-to-quint

4. In order to run the generated tests, you'll need to add some dependencies:
```bash
cargo add cw-multi-test@0.16.2 --dev
cargo add itf@0.2.4 --dev
cargo add anyhow@1.0.83 --dev
cargo add num-bigint@0.4.4 --dev
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,22 +191,25 @@ fn translate_items(tcx: TyCtxt, crate_name: &str, items: Vec<&rustc_hir::Item>)
.expect("Unable to write file");

// write tests to file
std::fs::create_dir_all("tests").expect("Unable to create directory");
std::fs::write(format!("tests/mbt_{}.rs", crate_name), tests).expect("Unable to write file");
}

// This is the main entry point for the plugin. It prints the generated quint code to STDOUT.
fn cosmwasm_to_quint(tcx: TyCtxt, _args: &CosmwasmToQuintPluginArgs) {
// create generated directory
// create directories for the output files (if they don't already exist)
std::fs::create_dir_all("quint/lib").expect("Unable to create directory");
std::fs::create_dir_all("tests").expect("Unable to create directory");

// Read quint lib files. `include_str!` makes it so they are read at
// compilation time, and therefore get embeded in the cosmwasm-to-quint executable
let bank = include_str!("./quint-lib-files/bank.qnt");
let basic_spells = include_str!("./quint-lib-files/basicSpells.qnt");
let bounded_uint = include_str!("./quint-lib-files/BoundedUInt.qnt");
let cw_types = include_str!("./quint-lib-files/cw_types.qnt");
let cw_utils = include_str!("./quint-lib-files/cw_utils.qnt");
let messaging = include_str!("./quint-lib-files/messaging.qnt");

// Write the lib files in runtime
std::fs::write("quint/lib/bank.qnt", bank).expect("Unable to write file");
std::fs::write("quint/lib/basicSpells.qnt", basic_spells).expect("Unable to write file");
std::fs::write("quint/lib/BoundedUInt.qnt", bounded_uint).expect("Unable to write file");
Expand Down
194 changes: 78 additions & 116 deletions src/test_generation/actions.rs
Original file line number Diff line number Diff line change
@@ -1,146 +1,108 @@
use crate::boilerplate::init_value_for_type;
use crate::test_generation::boilerplate::*;
use crate::types::{fallback_constructor, Context};
use itertools::Itertools;

pub fn translate_actions(ctx: Context) -> String {
let msgs = ctx
.message_type_for_action
ctx.message_type_for_action
.iter()
// Sort items by name so that the generated code is deterministic
.sorted_by(|a, b| a.0.cmp(b.0))
.map(|(action, ty)| {
if action == "instantiate" {
let msg_struct = ctx
.structs
.get("InstantiateMsg")
.cloned()
.unwrap_or_default();
let msg_fields = msg_struct
.iter()
.map(|f| {
let body = init_value_for_type(&ctx, f.ty.clone());

format!("{}: {}", f.name, body)
})
.collect_vec();
let msg = format!("InstantiateMsg {{ {} }}", msg_fields.join(", "));
let msg = generate_instantiate_msg(ctx.clone());
return translate_init(msg);
}
if action == "execute" || action == "instantiate" || action == "reply" {
return "".to_string();
}
let constructor = ctx
.constructors
.get(ty.as_str())
.cloned()
.unwrap_or_else(|| fallback_constructor(ty));

let nondet_picks = constructor
.fields
.iter()
.map(|f| {
let body = type_conversion(
format!("nondet_picks.message_{}.clone().unwrap()", f.name),
f.ty.clone(),
);

format!(" let message_{} = {};", f.name, body)
})
.collect_vec();

let fields = constructor
.fields
.iter()
.map(|f| format!("{}: message_{}", f.name, f.name))
.collect_vec()
.join(", ");
let msg = format!("{} {{ {} }}", constructor.name.replace('_', "::"), fields);

translate_action(action, msg, nondet_picks.clone())
});

msgs.clone().join("\n")

let msg = generate_message(ctx.clone(), ty.clone());
translate_action(action, msg)
})
.join("\n")
}

fn translate_action(action: &str, msg: String, nondet_picks: Vec<String>) -> String {
let header = format!(
fn translate_action(action_name: &str, msg: String) -> String {
format!(
"
\"{}_action\" => {{
let sender = Addr::unchecked(sender.unwrap());
let funds = funds_from_trace(amount, denom);
\"{action_name}_action\" => {{
{EXTRACT_SENDER_AND_FUNDS}
{msg}
{PRINT_MESSAGE_FIELDS}
{EXECUTE_CONTRACT}
{COMPARE_RESULT}
}}
"
)
}

fn translate_init(msg: String) -> String {
format!(
"
\"q::init\" => {{
println!(\"Initializing contract.\");
",
action
);
{EXTRACT_SENDER_AND_FUNDS}
{msg}
{PRINT_MESSAGE_FIELDS}
{INITIALIZE_CONTRACT}
{MINT_TOKENS}
}}
let footer = "
println!(\"Message: {:?}\", msg);
println!(\"Sender: {:?}\", sender);
println!(\"Funds: {:?}\", funds);
"
)
}

let res = app.execute_contract(
sender,
test_state.contract_addr.clone(),
&msg,
&funds,
);
fn generate_instantiate_msg(ctx: Context) -> String {
let msg_struct = ctx
.structs
.get("InstantiateMsg")
.cloned()
.unwrap_or_default();

compare_result(s.value.result.clone(), res)
}
let msg_fields = msg_struct
.iter()
.map(|f| {
let body = init_value_for_type(&ctx, f.ty.clone());

";
format!("{}: {}", f.name, body)
})
.collect_vec();

format!(
"{}{}\n let msg = {};{}",
header,
nondet_picks.clone().join("\n"),
msg,
footer,
)
format!("let msg = InstantiateMsg {{ {} }};", msg_fields.join(", "))
}

fn translate_init(msg: String) -> String {
let header = "
\"q::init\" => {
println!(\"Initializing contract.\");
fn generate_message(ctx: Context, ty: String) -> String {
let constructor = ctx
.constructors
.get(ty.as_str())
.cloned()
.unwrap_or_else(|| fallback_constructor(ty.as_ref()));

let sender = Addr::unchecked(sender.unwrap());
let funds = funds_from_trace(amount, denom);
let nondet_picks = constructor
.fields
.iter()
.map(|f| {
let body = type_conversion(
format!("nondet_picks.message_{}.clone().unwrap()", f.name),
f.ty.clone(),
);

format!(" let message_{} = {};", f.name, body)
})
.collect_vec()
.join("\n");

let fields = constructor
.fields
.iter()
.map(|f| format!("{}: message_{}", f.name, f.name))
.collect_vec()
.join(", ");

"
.to_string();

let footer = "
println!(\"Message: {:?}\", msg);
println!(\"Sender: {:?}\", sender);
println!(\"Funds: {:?}\", funds);
test_state.contract_addr = app.instantiate_contract(
code_id,
sender,
&msg,
&funds,
\"test\",
None,
).unwrap();
for (addr, coins) in s.value.bank.clone().iter() {
for (denom, amount) in coins.iter() {
app = mint_tokens(
app,
addr.clone(),
denom.to_string(),
Uint128::new(amount.to_u128().unwrap()),
);
}
}
}
";
let message_type = constructor.name.replace('_', "::");

format!(
"{}\n let msg = {};{}",
header, msg, footer,
"{nondet_picks}
let msg = {message_type} {{ {fields} }};"
)
}

Expand Down
41 changes: 41 additions & 0 deletions src/test_generation/boilerplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,44 @@ pub const TEST_FOOTER: &str = "
}
}
";

pub const PRINT_MESSAGE_FIELDS: &str = "println!(\"Message: {:?}\", msg);
println!(\"Sender: {:?}\", sender);
println!(\"Funds: {:?}\", funds);
";

pub const EXECUTE_CONTRACT: &str = "let res = app.execute_contract(
sender,
test_state.contract_addr.clone(),
&msg,
&funds,
);
";

pub const COMPARE_RESULT: &str = "compare_result(s.value.result.clone(), res)";

pub const EXTRACT_SENDER_AND_FUNDS: &str = "let sender = Addr::unchecked(sender.unwrap());
let funds = funds_from_trace(amount, denom);
";

pub const INITIALIZE_CONTRACT: &str = "test_state.contract_addr = app.instantiate_contract(
code_id,
sender,
&msg,
&funds,
\"test\",
None,
).unwrap();
";

pub const MINT_TOKENS: &str = "for (addr, coins) in s.value.bank.clone().iter() {
for (denom, amount) in coins.iter() {
app = mint_tokens(
app,
addr.clone(),
denom.to_string(),
Uint128::new(amount.to_u128().unwrap()),
);
}
}
";
6 changes: 2 additions & 4 deletions tests/snapshots/integration_tests__ctf01.snap
Original file line number Diff line number Diff line change
Expand Up @@ -361,14 +361,12 @@ pub mod tests {
}



"q::init" => {
println!("Initializing contract.");

let sender = Addr::unchecked(sender.unwrap());
let funds = funds_from_trace(amount, denom);


let msg = InstantiateMsg { count: 0 };
println!("Message: {:?}", msg);
println!("Sender: {:?}", sender);
Expand All @@ -393,7 +391,8 @@ pub mod tests {
);
}
}
}

}



Expand All @@ -417,7 +416,6 @@ pub mod tests {
compare_result(s.value.result.clone(), res)
}


_ => panic!("Invalid action taken"),
}
compare_state(&test_state, &app, &(s.value.clone()));
Expand Down
8 changes: 2 additions & 6 deletions tests/snapshots/integration_tests__ctf02.snap
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,12 @@ pub mod tests {
}



"q::init" => {
println!("Initializing contract.");

let sender = Addr::unchecked(sender.unwrap());
let funds = funds_from_trace(amount, denom);


let msg = InstantiateMsg { };
println!("Message: {:?}", msg);
println!("Sender: {:?}", sender);
Expand All @@ -418,7 +416,8 @@ pub mod tests {
);
}
}
}

}



Expand All @@ -443,7 +442,6 @@ pub mod tests {
}



"unstake_action" => {
let sender = Addr::unchecked(sender.unwrap());
let funds = funds_from_trace(amount, denom);
Expand All @@ -465,7 +463,6 @@ pub mod tests {
}



"withdraw_action" => {
let sender = Addr::unchecked(sender.unwrap());
let funds = funds_from_trace(amount, denom);
Expand All @@ -486,7 +483,6 @@ pub mod tests {
compare_result(s.value.result.clone(), res)
}


_ => panic!("Invalid action taken"),
}
compare_state(&test_state, &app, &(s.value.clone()));
Expand Down
Loading

0 comments on commit d6990e6

Please sign in to comment.