mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-13 08:40:45 +08:00
feat: add coverage flags to clarity-cli
This commit is contained in:
@@ -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, ®ister_files, &coverage_files)
|
||||
.expect("Failed to produce an lcov output");
|
||||
(0, None)
|
||||
}
|
||||
_ => {
|
||||
print_usage(invoked_by);
|
||||
(1, None)
|
||||
|
||||
Reference in New Issue
Block a user