feat: add coverage flags to clarity-cli

This commit is contained in:
Hank Stoever
2022-06-07 09:06:37 -07:00
parent 50c716f91c
commit 8bf9fcd6f1

View File

@@ -16,6 +16,7 @@
use std::convert::TryInto;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::io::{Read, Write};
@@ -23,6 +24,8 @@ use std::iter::Iterator;
use std::path::PathBuf;
use std::process;
use clarity::util::get_epoch_time_ms;
use clarity::vm::coverage::CoverageReporter;
use rand::Rng;
use rusqlite::types::ToSql;
use rusqlite::Row;
@@ -421,6 +424,7 @@ pub fn vm_execute(program: &str) -> Result<Option<Value>, Error> {
LimitedCostTracker::new_free(),
DEFAULT_CLI_EPOCH,
);
global_context.coverage_reporting = Some(CoverageReporter::new());
global_context.execute(|g| {
let parsed = ast::build_ast(&contract_id, program, &mut ())?.expressions;
eval_all(&parsed, &mut contract_context, g)
@@ -1237,6 +1241,11 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
} else {
false
};
let coverage_folder = if let Ok(covarg) = consume_arg(&mut argv, &["--c"], true) {
covarg
} else {
None
};
let evalInput = get_eval_input(invoked_by, &argv);
let vm_filename = if argv.len() == 3 { &argv[2] } else { &argv[3] };
@@ -1246,18 +1255,39 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
MarfedKV::open(vm_filename, None, None),
"Failed to open VM database.",
);
let mainnet = header_db.is_mainnet();
let result_and_cost = at_chaintip(vm_filename, marf_kv, |mut marf| {
let result_and_cost = with_env_costs(mainnet, &header_db, &mut marf, |vm_env| {
vm_env
.get_exec_environment(None)
.eval_read_only(&evalInput.contract_identifier, &evalInput.content)
if coverage_folder.is_some() {
vm_env.set_coverage_reporter(CoverageReporter::new());
}
(
vm_env
.get_exec_environment(None)
.eval_read_only(&evalInput.contract_identifier, &evalInput.content),
vm_env.take_coverage_reporter(),
)
});
(marf, result_and_cost)
let ((result, coverage), cost) = result_and_cost;
(marf, (result, cost, coverage))
});
match result_and_cost {
(Ok(result), cost) => {
(Ok(result), cost, coverage) => {
if let Some(ref coverage_folder) = coverage_folder {
let mut coverage_file = PathBuf::from(coverage_folder);
coverage_file.push(&format!("eval_{}", get_epoch_time_ms()));
coverage_file.set_extension("clarcov");
coverage
.expect(
"Failed to recover coverage reporter when coverage was requested",
)
.to_file(&coverage_file)
.expect("Coverage reference file generation failure");
}
let mut result_json = json!({
"output": serde_json::to_value(&result).unwrap(),
"success": true,
@@ -1268,7 +1298,19 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
(0, Some(result_json))
}
(Err(error), cost) => {
(Err(error), cost, coverage) => {
if let Some(ref coverage_folder) = coverage_folder {
let mut coverage_file = PathBuf::from(coverage_folder);
coverage_file.push(&format!("eval_{}", get_epoch_time_ms()));
coverage_file.set_extension("clarcov");
coverage
.expect(
"Failed to recover coverage reporter when coverage was requested",
)
.to_file(&coverage_file)
.expect("Coverage reference file generation failure");
}
let mut result_json = json!({
"error": {
"runtime": serde_json::to_value(&format!("{}", error)).unwrap()
@@ -1357,6 +1399,11 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
}
"launch" => {
let mut argv: Vec<String> = args.into_iter().map(|x| x.clone()).collect();
let coverage_folder = if let Ok(covarg) = consume_arg(&mut argv, &["--c"], true) {
covarg
} else {
None
};
let costs = if let Ok(Some(_)) = consume_arg(&mut argv, &["--costs"], false) {
true
} else {
@@ -1382,14 +1429,15 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
}
let vm_filename = &argv[3];
let contract_src_file = &args[2];
let contract_identifier = friendly_expect(
QualifiedContractIdentifier::parse(&argv[1]),
"Failed to parse contract identifier.",
);
let contract_content: String = friendly_expect(
fs::read_to_string(&argv[2]),
&format!("Error reading file: {}", argv[2]),
fs::read_to_string(contract_src_file),
&format!("Error reading file: {}", contract_src_file),
);
// TODO: Add --clarity_version as command line argument
@@ -1397,6 +1445,22 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
parse(&contract_identifier, &contract_content),
"Failed to parse program.",
);
if let Some(ref coverage_folder) = coverage_folder {
let mut coverage_file = PathBuf::from(coverage_folder);
coverage_file.push(&format!("launch_{}", get_epoch_time_ms()));
coverage_file.set_extension("clarcovref");
CoverageReporter::register_src_file(
&contract_identifier,
contract_src_file,
&ast,
&coverage_file,
)
.expect("Coverage reference file generation failure");
}
// let header_db = CLIHeadersDB::new(vm_filename, false);
let header_db =
friendly_expect(CLIHeadersDB::resume(vm_filename), "Failed to open CLI DB");
let marf_kv = friendly_expect(
@@ -1414,16 +1478,26 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
Ok(analysis) => {
let result_and_cost =
with_env_costs(mainnet, &header_db, &mut marf, |vm_env| {
vm_env
.initialize_contract(contract_identifier, &contract_content)
if coverage_folder.is_some() {
vm_env.set_coverage_reporter(CoverageReporter::new());
}
(
vm_env.initialize_contract(
contract_identifier,
&contract_content,
),
vm_env.take_coverage_reporter(),
)
});
(header_db, marf, Ok((analysis, result_and_cost)))
// let (result)
let ((result, coverage), cost) = result_and_cost;
(header_db, marf, Ok((analysis, (result, cost, coverage))))
}
}
});
match analysis_result_and_cost {
Ok((contract_analysis, (Ok((_x, asset_map, events)), cost))) => {
Ok((contract_analysis, (Ok((_x, asset_map, events)), cost, coverage))) => {
let mut result = json!({
"message": "Contract initialized!"
});
@@ -1431,6 +1505,19 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
add_costs(&mut result, costs, cost);
add_assets(&mut result, assets, asset_map);
if let Some(ref coverage_folder) = coverage_folder {
let mut coverage_file = PathBuf::from(coverage_folder);
coverage_file.push(&format!("launch_{}", get_epoch_time_ms()));
coverage_file.set_extension("clarcov");
coverage
.expect(
"Failed to recover coverage reporter when coverage was requested",
)
.to_file(&coverage_file)
.expect("Coverage reference file generation failure");
}
if output_analysis {
result["analysis"] =
serde_json::to_value(&build_contract_interface(&contract_analysis))
@@ -1467,6 +1554,11 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
}
"execute" => {
let mut argv: Vec<String> = args.into_iter().map(|x| x.clone()).collect();
let coverage_folder = if let Ok(covarg) = consume_arg(&mut argv, &["--c"], true) {
covarg
} else {
None
};
let costs = if let Ok(Some(_)) = consume_arg(&mut argv, &["--costs"], false) {
true
@@ -1526,14 +1618,36 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
let (_, _, result_and_cost) = in_block(header_db, marf_kv, |header_db, mut marf| {
let result_and_cost = with_env_costs(mainnet, &header_db, &mut marf, |vm_env| {
vm_env.execute_transaction(sender, contract_identifier, &tx_name, &arguments)
if coverage_folder.is_some() {
vm_env.set_coverage_reporter(CoverageReporter::new());
}
(
vm_env.execute_transaction(
sender,
contract_identifier,
&tx_name,
&arguments,
),
vm_env.take_coverage_reporter(),
)
});
(header_db, marf, result_and_cost)
let ((result, coverage), cost) = result_and_cost;
(header_db, marf, (result, cost, coverage))
});
match result_and_cost {
(Ok((x, asset_map, events)), cost) => {
(Ok((x, asset_map, events)), cost, coverage) => {
if let Value::Response(data) = x {
if let Some(ref coverage_folder) = coverage_folder {
let mut coverage_file = PathBuf::from(coverage_folder);
coverage_file.push(&format!("execute_{}", get_epoch_time_ms()));
coverage_file.set_extension("clarcov");
coverage
.expect("Failed to recover coverage reporter when coverage was requested")
.to_file(&coverage_file)
.expect("Coverage reference file generation failure");
}
if data.committed {
let mut result = json!({
"message": "Transaction executed and committed.",
@@ -1576,7 +1690,7 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
(1, Some(result))
}
}
(Err(error), _) => {
(Err(error), ..) => {
let result = json!({
"error": {
"runtime": "Transaction execution error.",
@@ -1588,6 +1702,29 @@ pub fn invoke_command(invoked_by: &str, args: &[String]) -> (i32, Option<serde_j
}
}
}
"make_lcov" => {
let mut register_files = vec![];
let mut coverage_files = vec![];
let coverage_folder = &args[1];
let lcov_output_file = &args[2];
for folder_entry in
fs::read_dir(coverage_folder).expect("Failed to read the coverage folder")
{
let folder_entry =
folder_entry.expect("Failed to read entry in the coverage folder");
let entry_path = folder_entry.path();
if entry_path.is_file() {
if entry_path.extension() == Some(OsStr::new("clarcovref")) {
register_files.push(entry_path)
} else if entry_path.extension() == Some(OsStr::new("clarcov")) {
coverage_files.push(entry_path)
}
}
}
CoverageReporter::produce_lcov(lcov_output_file, &register_files, &coverage_files)
.expect("Failed to produce an lcov output");
(0, None)
}
_ => {
print_usage(invoked_by);
(1, None)