mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-23 23:23:50 +03:00
Update compiler test framework
This commit is contained in:
parent
acc358bbb4
commit
98bbcacebb
@ -26,10 +26,11 @@ use leo_test_framework::{
|
||||
|
||||
use snarkvm::prelude::*;
|
||||
|
||||
use crate::utilities::{get_cwd_option, hash_asts, hash_content, setup_build_directory};
|
||||
use crate::utilities::{get_build_options, get_cwd_option, hash_asts, hash_content, setup_build_directory};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::Value;
|
||||
use std::{fs, path::Path, rc::Rc};
|
||||
use leo_compiler::{CompilerOptions, OutputOptions};
|
||||
|
||||
struct CompileNamespace;
|
||||
|
||||
@ -61,37 +62,60 @@ fn run_test(test: Test, handler: &Handler) -> Result<Value, ()> {
|
||||
// Check for CWD option:
|
||||
let cwd = get_cwd_option(&test);
|
||||
|
||||
// Parse the program.
|
||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
||||
// Extract the compiler build configurations from the config file.
|
||||
let build_options = get_build_options(&test.config);
|
||||
|
||||
// Compile the program to bytecode.
|
||||
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
let mut outputs = Vec::with_capacity(build_options.len());
|
||||
|
||||
// Set up the build directory.
|
||||
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
||||
for build in build_options {
|
||||
let compiler_options = CompilerOptions {
|
||||
build,
|
||||
output: OutputOptions {
|
||||
spans_enabled: false,
|
||||
initial_input_ast: true,
|
||||
initial_ast: true,
|
||||
unrolled_ast: true,
|
||||
ssa_ast: true,
|
||||
flattened_ast: true,
|
||||
inlined_ast: true,
|
||||
dce_ast: true,
|
||||
}
|
||||
};
|
||||
|
||||
// Get the program process and check all instructions.
|
||||
handler.extend_if_error(package.get_process().map_err(LeoError::Anyhow))?;
|
||||
// Parse the program.
|
||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd.clone(), Some(compiler_options)))?;
|
||||
|
||||
// Hash the ast files.
|
||||
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) = hash_asts();
|
||||
// Compile the program to bytecode.
|
||||
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
|
||||
// Clean up the output directory.
|
||||
if fs::read_dir("/tmp/output").is_ok() {
|
||||
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
|
||||
// Set up the build directory.
|
||||
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
||||
|
||||
// Get the program process and check all instructions.
|
||||
handler.extend_if_error(package.get_process().map_err(LeoError::Anyhow))?;
|
||||
|
||||
// Hash the ast files.
|
||||
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) = hash_asts();
|
||||
|
||||
// Clean up the output directory.
|
||||
if fs::read_dir("/tmp/output").is_ok() {
|
||||
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
|
||||
}
|
||||
|
||||
let final_output = CompileOutput {
|
||||
initial_ast,
|
||||
unrolled_ast,
|
||||
ssa_ast,
|
||||
flattened_ast,
|
||||
inlined_ast,
|
||||
dce_ast,
|
||||
bytecode: hash_content(&bytecode),
|
||||
};
|
||||
|
||||
outputs.push(final_output);
|
||||
}
|
||||
|
||||
let final_output = CompileOutput {
|
||||
initial_ast,
|
||||
unrolled_ast,
|
||||
ssa_ast,
|
||||
flattened_ast,
|
||||
inlined_ast,
|
||||
dce_ast,
|
||||
bytecode: hash_content(&bytecode),
|
||||
};
|
||||
Ok(serde_yaml::to_value(final_output).expect("serialization failed"))
|
||||
Ok(serde_yaml::to_value(outputs).expect("serialization failed"))
|
||||
}
|
||||
|
||||
struct TestRunner;
|
||||
|
@ -26,7 +26,7 @@ use utilities::{
|
||||
Network,
|
||||
};
|
||||
|
||||
use crate::utilities::{hash_asts, hash_content};
|
||||
use crate::utilities::{get_build_options, hash_asts, hash_content};
|
||||
|
||||
use leo_errors::emitter::Handler;
|
||||
use leo_span::symbol::create_session_if_not_set_then;
|
||||
@ -42,6 +42,7 @@ use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::Value;
|
||||
use std::{collections::BTreeMap, fs, path::Path, rc::Rc};
|
||||
use leo_compiler::{CompilerOptions, OutputOptions};
|
||||
|
||||
// TODO: Evaluate namespace.
|
||||
struct ExecuteNamespace;
|
||||
@ -81,103 +82,125 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
// Check for CWD option:
|
||||
let cwd = get_cwd_option(&test);
|
||||
|
||||
// Parse the program.
|
||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
||||
// Extract the compiler build configurations from the config file.
|
||||
let build_options = get_build_options(&test.config);
|
||||
|
||||
// Compile the program to bytecode.
|
||||
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
let mut outputs = Vec::with_capacity(build_options.len());
|
||||
|
||||
// Extract the cases from the test config.
|
||||
let all_cases =
|
||||
test.config.extra.get("cases").expect("An `Execute` config must have a `cases` field.").as_mapping().unwrap();
|
||||
for build in build_options {
|
||||
let compiler_options = CompilerOptions {
|
||||
build,
|
||||
output: OutputOptions {
|
||||
spans_enabled: false,
|
||||
initial_input_ast: true,
|
||||
initial_ast: true,
|
||||
unrolled_ast: true,
|
||||
ssa_ast: true,
|
||||
flattened_ast: true,
|
||||
inlined_ast: true,
|
||||
dce_ast: true,
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize a map for the expected results.
|
||||
let mut results = BTreeMap::new();
|
||||
// Parse the program.
|
||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd.clone(), Some(compiler_options)))?;
|
||||
|
||||
// Setup the build directory.
|
||||
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
||||
// Compile the program to bytecode.
|
||||
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
|
||||
// Initialize an rng.
|
||||
let rng = &mut rand::thread_rng();
|
||||
// Extract the cases from the test config.
|
||||
let all_cases =
|
||||
test.config.extra.get("cases").expect("An `Execute` config must have a `cases` field.").as_mapping().unwrap();
|
||||
|
||||
// Run each test case for each function.
|
||||
for (function_name, function_cases) in all_cases {
|
||||
let function_name = Identifier::from_str(function_name.as_str().unwrap()).unwrap();
|
||||
let cases = function_cases.as_sequence().unwrap();
|
||||
let mut function_results = Vec::with_capacity(cases.len());
|
||||
// Initialize a map for the expected results.
|
||||
let mut results = BTreeMap::new();
|
||||
|
||||
for case in cases {
|
||||
let case = case.as_mapping().unwrap();
|
||||
let inputs: Vec<_> = case
|
||||
.get(&Value::from("input"))
|
||||
.unwrap()
|
||||
.as_sequence()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|input| console::program::Value::<Network>::from_str(input.as_str().unwrap()).unwrap())
|
||||
.collect();
|
||||
let input_string = format!("[{}]", inputs.iter().map(|input| input.to_string()).join(", "));
|
||||
// Setup the build directory.
|
||||
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
||||
|
||||
// TODO: Add support for custom config like custom private keys.
|
||||
// Execute the program and get the outputs.
|
||||
let output_string = match package.run::<Aleo, _>(
|
||||
None,
|
||||
package.manifest_file().development_private_key(),
|
||||
function_name,
|
||||
&inputs,
|
||||
rng,
|
||||
) {
|
||||
Ok((response, _, _, _)) => format!(
|
||||
"[{}]",
|
||||
response
|
||||
.outputs()
|
||||
.iter()
|
||||
.map(|output| {
|
||||
match output {
|
||||
// Remove the `_nonce` from the record string.
|
||||
console::program::Value::Record(record) => {
|
||||
let pattern = Regex::new(r"_nonce: \d+group.public").unwrap();
|
||||
pattern.replace(&record.to_string(), "").to_string()
|
||||
// Initialize an rng.
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
// Run each test case for each function.
|
||||
for (function_name, function_cases) in all_cases {
|
||||
let function_name = Identifier::from_str(function_name.as_str().unwrap()).unwrap();
|
||||
let cases = function_cases.as_sequence().unwrap();
|
||||
let mut function_results = Vec::with_capacity(cases.len());
|
||||
|
||||
for case in cases {
|
||||
let case = case.as_mapping().unwrap();
|
||||
let inputs: Vec<_> = case
|
||||
.get(&Value::from("input"))
|
||||
.unwrap()
|
||||
.as_sequence()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|input| console::program::Value::<Network>::from_str(input.as_str().unwrap()).unwrap())
|
||||
.collect();
|
||||
let input_string = format!("[{}]", inputs.iter().map(|input| input.to_string()).join(", "));
|
||||
|
||||
// TODO: Add support for custom config like custom private keys.
|
||||
// Execute the program and get the outputs.
|
||||
let output_string = match package.run::<Aleo, _>(
|
||||
None,
|
||||
package.manifest_file().development_private_key(),
|
||||
function_name,
|
||||
&inputs,
|
||||
rng,
|
||||
) {
|
||||
Ok((response, _, _, _)) => format!(
|
||||
"[{}]",
|
||||
response
|
||||
.outputs()
|
||||
.iter()
|
||||
.map(|output| {
|
||||
match output {
|
||||
// Remove the `_nonce` from the record string.
|
||||
console::program::Value::Record(record) => {
|
||||
let pattern = Regex::new(r"_nonce: \d+group.public").unwrap();
|
||||
pattern.replace(&record.to_string(), "").to_string()
|
||||
}
|
||||
_ => output.to_string(),
|
||||
}
|
||||
_ => output.to_string(),
|
||||
}
|
||||
})
|
||||
.join(", ")
|
||||
),
|
||||
Err(err) => format!("SnarkVMError({err})"),
|
||||
};
|
||||
})
|
||||
.join(", ")
|
||||
),
|
||||
Err(err) => format!("SnarkVMError({err})"),
|
||||
};
|
||||
|
||||
// Store the inputs and outputs in a map.
|
||||
let mut result = BTreeMap::new();
|
||||
result.insert("input".to_string(), input_string);
|
||||
result.insert("output".to_string(), output_string);
|
||||
// Store the inputs and outputs in a map.
|
||||
let mut result = BTreeMap::new();
|
||||
result.insert("input".to_string(), input_string);
|
||||
result.insert("output".to_string(), output_string);
|
||||
|
||||
// Add the hashes of the inputs and outputs to the function results.
|
||||
function_results.push(result);
|
||||
// Add the hashes of the inputs and outputs to the function results.
|
||||
function_results.push(result);
|
||||
}
|
||||
results.insert(function_name.to_string(), function_results);
|
||||
}
|
||||
results.insert(function_name.to_string(), function_results);
|
||||
|
||||
// Hash the ast files.
|
||||
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) = hash_asts();
|
||||
|
||||
// Clean up the output directory.
|
||||
if fs::read_dir("/tmp/output").is_ok() {
|
||||
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
|
||||
}
|
||||
|
||||
let final_output = ExecuteOutput {
|
||||
initial_ast,
|
||||
unrolled_ast,
|
||||
ssa_ast,
|
||||
flattened_ast,
|
||||
inlined_ast,
|
||||
dce_ast,
|
||||
bytecode: hash_content(&bytecode),
|
||||
results,
|
||||
};
|
||||
outputs.push(final_output);
|
||||
}
|
||||
|
||||
// Hash the ast files.
|
||||
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) = hash_asts();
|
||||
|
||||
// Clean up the output directory.
|
||||
if fs::read_dir("/tmp/output").is_ok() {
|
||||
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
|
||||
}
|
||||
|
||||
let final_output = ExecuteOutput {
|
||||
initial_ast,
|
||||
unrolled_ast,
|
||||
ssa_ast,
|
||||
flattened_ast,
|
||||
inlined_ast,
|
||||
dce_ast,
|
||||
bytecode: hash_content(&bytecode),
|
||||
results,
|
||||
};
|
||||
Ok(serde_yaml::to_value(final_output).expect("serialization failed"))
|
||||
Ok(serde_yaml::to_value(outputs).expect("serialization failed"))
|
||||
}
|
||||
|
||||
struct TestRunner;
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use leo_compiler::{Compiler, CompilerOptions};
|
||||
use leo_compiler::{BuildOptions, Compiler, CompilerOptions};
|
||||
use leo_errors::{
|
||||
emitter::{Buffer, Emitter, Handler},
|
||||
LeoError,
|
||||
@ -34,6 +34,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
use leo_test_framework::test::TestConfig;
|
||||
|
||||
pub type Network = Testnet3;
|
||||
#[allow(unused)]
|
||||
@ -62,6 +63,22 @@ pub fn get_cwd_option(test: &Test) -> Option<PathBuf> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_build_options(test_config: &TestConfig) -> Vec<BuildOptions> {
|
||||
match test_config.extra.get("configs") {
|
||||
Some(configs) => {
|
||||
// Parse the sequence of compiler configurations.
|
||||
configs.as_sequence().unwrap().iter().map(|config| {
|
||||
let config = config.as_mapping().unwrap();
|
||||
assert_eq!(config.len(), 1, "A compiler configuration must have exactly one key-value pair. e.g. `dce_enabled`: true");
|
||||
BuildOptions {
|
||||
dce_enabled: config.get(&serde_yaml::Value::String("dce_enabled".to_string())).expect("Expected key `dce_enabled`").as_bool().unwrap(),
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
None => vec![ BuildOptions { dce_enabled: true } ],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_build_directory(program_name: &str, bytecode: &String, handler: &Handler) -> Result<Package<Network>, ()> {
|
||||
// Initialize a temporary directory.
|
||||
let directory = temp_dir();
|
||||
@ -85,7 +102,7 @@ pub fn setup_build_directory(program_name: &str, bytecode: &String, handler: &Ha
|
||||
handler.extend_if_error(Package::<Testnet3>::open(&directory).map_err(LeoError::Anyhow))
|
||||
}
|
||||
|
||||
pub fn new_compiler(handler: &Handler, main_file_path: PathBuf) -> Compiler<'_> {
|
||||
pub fn new_compiler(handler: &Handler, main_file_path: PathBuf, compiler_options: Option<CompilerOptions>) -> Compiler<'_> {
|
||||
let output_dir = PathBuf::from("/tmp/output/");
|
||||
fs::create_dir_all(output_dir.clone()).unwrap();
|
||||
|
||||
@ -95,17 +112,7 @@ pub fn new_compiler(handler: &Handler, main_file_path: PathBuf) -> Compiler<'_>
|
||||
handler,
|
||||
main_file_path,
|
||||
output_dir,
|
||||
Some(CompilerOptions {
|
||||
spans_enabled: false,
|
||||
dce_enabled: true,
|
||||
initial_input_ast: true,
|
||||
initial_ast: true,
|
||||
unrolled_ast: true,
|
||||
ssa_ast: true,
|
||||
flattened_ast: true,
|
||||
inlined_ast: true,
|
||||
dce_ast: true,
|
||||
}),
|
||||
compiler_options
|
||||
)
|
||||
}
|
||||
|
||||
@ -113,8 +120,9 @@ pub fn parse_program<'a>(
|
||||
handler: &'a Handler,
|
||||
program_string: &str,
|
||||
cwd: Option<PathBuf>,
|
||||
compiler_options: Option<CompilerOptions>,
|
||||
) -> Result<Compiler<'a>, LeoError> {
|
||||
let mut compiler = new_compiler(handler, cwd.clone().unwrap_or_else(|| "compiler-test".into()));
|
||||
let mut compiler = new_compiler(handler, cwd.clone().unwrap_or_else(|| "compiler-test".into()), compiler_options);
|
||||
let name = cwd.map_or_else(|| FileName::Custom("compiler-test".into()), FileName::Real);
|
||||
compiler.parse_program_from_string(program_string, name)?;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user