use crate::run;
use leo_ast::{NodeBuilder, Stub};
use leo_errors::{BufferEmitter, Handler, Result};
use leo_span::{Symbol, create_session_if_not_set_then};
use indexmap::IndexMap;
use itertools::Itertools as _;
use std::{fmt::Write as _, rc::Rc};
#[derive(Debug)]
struct Config {
seed: u64,
start_height: Option<u32>,
sources: Vec<String>,
}
impl Default for Config {
fn default() -> Self {
Self { seed: 1234567890, start_height: None, sources: Vec::new() }
}
}
fn execution_run_test(
config: &Config,
cases: &[run::Case],
handler: &Handler,
node_builder: &Rc<NodeBuilder>,
) -> Result<String> {
let mut import_stubs = IndexMap::new();
let mut ledger_config =
run::Config { seed: config.seed, start_height: config.start_height, programs: Vec::new(), skip_proving: true };
let (last, rest) = config.sources.split_last().expect("non-empty sources");
for source in rest {
if let Some((library_name, library_source)) = super::test_utils::extract_library_header(source) {
let library = super::test_utils::parse_library(&library_name, library_source, handler, node_builder)?;
import_stubs.insert(Symbol::intern(&library_name), library.into());
} else {
let (program, program_name) =
super::test_utils::parse_program(source, handler, node_builder, import_stubs.clone())?;
import_stubs.insert(Symbol::intern(&program_name), program.into());
}
}
let final_program_name = super::test_utils::extract_program_name(last, handler)?;
let final_symbol = Symbol::intern(&final_program_name);
{
let symbols: Vec<Symbol> = import_stubs.keys().copied().collect();
for (i, symbol) in symbols.iter().enumerate() {
if let Some(Stub::FromLibrary { parents, .. }) = import_stubs.get_mut(symbol) {
parents.extend(symbols[i + 1..].iter().copied());
parents.insert(final_symbol);
}
}
}
let (compiled, program_name) = super::test_utils::whole_compile(last, handler, node_builder, import_stubs.clone())?;
let mut requires_ledger = false;
for import in &compiled.imports {
requires_ledger |= import.bytecode.contains("async");
ledger_config.programs.push(run::Program { bytecode: import.bytecode.clone(), name: import.name.clone() });
}
let primary_bytecode = compiled.primary.bytecode.clone();
requires_ledger |= primary_bytecode.contains("async");
ledger_config.programs.push(run::Program { bytecode: primary_bytecode, name: program_name });
let mut result = ledger_config
.programs
.clone()
.into_iter()
.map(|program| program.bytecode)
.format(&format!("{}\n", super::test_utils::PROGRAM_DELIMITER))
.to_string();
if requires_ledger {
let outcomes =
run::run_with_ledger(&ledger_config, &[cases.to_vec()])?.into_iter().flatten().collect::<Vec<_>>();
assert_eq!(outcomes.len(), cases.len());
for outcome in outcomes {
write!(result, "verified: {}\nstatus: {}\n", outcome.verified, outcome.status).unwrap();
writeln!(result, "{}\n", outcome.execution).unwrap();
}
} else {
let outcomes = run::run_without_ledger(&ledger_config, cases)?;
assert_eq!(outcomes.len(), cases.len());
for outcome in outcomes {
write!(result, "status: {}\noutput: {}\n", outcome.status, outcome.outcome.output).unwrap();
}
}
Ok(result)
}
fn execution_runner(source: &str) -> String {
let buf = BufferEmitter::new();
let handler = Handler::new(buf.clone());
let node_builder = Rc::new(NodeBuilder::default());
let mut config = Config::default();
let mut cases = Vec::<run::Case>::new();
let re_input = regex::Regex::new(r#""([^"]+)""#).unwrap();
for line in source.lines() {
if line.starts_with("[case]") {
cases.push(Default::default());
} else if let Some(rest) = line.strip_prefix("program = ") {
cases.last_mut().unwrap().program_name = rest.trim_matches('"').into();
} else if let Some(rest) = line.strip_prefix("function = ") {
cases.last_mut().unwrap().function = rest.trim_matches('"').into();
} else if let Some(rest) = line.strip_prefix("private_key = ") {
cases.last_mut().unwrap().private_key = Some(rest.trim_matches('"').into());
} else if let Some(rest) = line.strip_prefix("input = ") {
cases.last_mut().unwrap().input = re_input.captures_iter(rest).map(|s| s[1].to_string()).collect();
} else if let Some(rest) = line.strip_prefix("seed = ") {
config.seed = rest.parse::<u64>().unwrap();
} else if let Some(rest) = line.strip_prefix("start_height = ") {
config.start_height = Some(rest.parse::<u32>().unwrap())
}
}
config.sources = source.split(super::test_utils::PROGRAM_DELIMITER).map(|s| s.trim().to_string()).collect();
create_session_if_not_set_then(|_| match execution_run_test(&config, &cases, &handler, &node_builder) {
Ok(s) => s,
Err(e) => {
format!("Error while running execution tests:\n{e}\n\nErrors:\n{}", buf.extract_errs())
}
})
}
#[cfg(test)]
mod execution_tests {
include!(concat!(env!("OUT_DIR"), "/execution_tests.rs"));
}