mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-11 04:49:15 +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 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::{Deserialize, Serialize};
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
use std::{fs, path::Path, rc::Rc};
|
use std::{fs, path::Path, rc::Rc};
|
||||||
|
use leo_compiler::{CompilerOptions, OutputOptions};
|
||||||
|
|
||||||
struct CompileNamespace;
|
struct CompileNamespace;
|
||||||
|
|
||||||
@ -61,37 +62,60 @@ fn run_test(test: Test, handler: &Handler) -> Result<Value, ()> {
|
|||||||
// Check for CWD option:
|
// Check for CWD option:
|
||||||
let cwd = get_cwd_option(&test);
|
let cwd = get_cwd_option(&test);
|
||||||
|
|
||||||
// Parse the program.
|
// Extract the compiler build configurations from the config file.
|
||||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
let build_options = get_build_options(&test.config);
|
||||||
|
|
||||||
// Compile the program to bytecode.
|
let mut outputs = Vec::with_capacity(build_options.len());
|
||||||
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
|
||||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
|
||||||
|
|
||||||
// Set up the build directory.
|
for build in build_options {
|
||||||
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
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.
|
// Parse the program.
|
||||||
handler.extend_if_error(package.get_process().map_err(LeoError::Anyhow))?;
|
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd.clone(), Some(compiler_options)))?;
|
||||||
|
|
||||||
// Hash the ast files.
|
// Compile the program to bytecode.
|
||||||
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, inlined_ast, dce_ast) = hash_asts();
|
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.
|
// Set up the build directory.
|
||||||
if fs::read_dir("/tmp/output").is_ok() {
|
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
||||||
fs::remove_dir_all(Path::new("/tmp/output")).expect("Error failed to clean up output dir.");
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
Ok(serde_yaml::to_value(outputs).expect("serialization failed"))
|
||||||
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"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestRunner;
|
struct TestRunner;
|
||||||
|
@ -26,7 +26,7 @@ use utilities::{
|
|||||||
Network,
|
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_errors::emitter::Handler;
|
||||||
use leo_span::symbol::create_session_if_not_set_then;
|
use leo_span::symbol::create_session_if_not_set_then;
|
||||||
@ -42,6 +42,7 @@ use regex::Regex;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
use std::{collections::BTreeMap, fs, path::Path, rc::Rc};
|
use std::{collections::BTreeMap, fs, path::Path, rc::Rc};
|
||||||
|
use leo_compiler::{CompilerOptions, OutputOptions};
|
||||||
|
|
||||||
// TODO: Evaluate namespace.
|
// TODO: Evaluate namespace.
|
||||||
struct ExecuteNamespace;
|
struct ExecuteNamespace;
|
||||||
@ -81,103 +82,125 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
|||||||
// Check for CWD option:
|
// Check for CWD option:
|
||||||
let cwd = get_cwd_option(&test);
|
let cwd = get_cwd_option(&test);
|
||||||
|
|
||||||
// Parse the program.
|
// Extract the compiler build configurations from the config file.
|
||||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
let build_options = get_build_options(&test.config);
|
||||||
|
|
||||||
// Compile the program to bytecode.
|
let mut outputs = Vec::with_capacity(build_options.len());
|
||||||
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
|
||||||
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
|
||||||
|
|
||||||
// Extract the cases from the test config.
|
for build in build_options {
|
||||||
let all_cases =
|
let compiler_options = CompilerOptions {
|
||||||
test.config.extra.get("cases").expect("An `Execute` config must have a `cases` field.").as_mapping().unwrap();
|
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.
|
// Parse the program.
|
||||||
let mut results = BTreeMap::new();
|
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd.clone(), Some(compiler_options)))?;
|
||||||
|
|
||||||
// Setup the build directory.
|
// Compile the program to bytecode.
|
||||||
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
let program_name = format!("{}.{}", parsed.program_name, parsed.network);
|
||||||
|
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||||
|
|
||||||
// Initialize an rng.
|
// Extract the cases from the test config.
|
||||||
let rng = &mut rand::thread_rng();
|
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.
|
// Initialize a map for the expected results.
|
||||||
for (function_name, function_cases) in all_cases {
|
let mut results = BTreeMap::new();
|
||||||
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 {
|
// Setup the build directory.
|
||||||
let case = case.as_mapping().unwrap();
|
let package = setup_build_directory(&program_name, &bytecode, handler)?;
|
||||||
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.
|
// Initialize an rng.
|
||||||
// Execute the program and get the outputs.
|
let rng = &mut rand::thread_rng();
|
||||||
let output_string = match package.run::<Aleo, _>(
|
|
||||||
None,
|
// Run each test case for each function.
|
||||||
package.manifest_file().development_private_key(),
|
for (function_name, function_cases) in all_cases {
|
||||||
function_name,
|
let function_name = Identifier::from_str(function_name.as_str().unwrap()).unwrap();
|
||||||
&inputs,
|
let cases = function_cases.as_sequence().unwrap();
|
||||||
rng,
|
let mut function_results = Vec::with_capacity(cases.len());
|
||||||
) {
|
|
||||||
Ok((response, _, _, _)) => format!(
|
for case in cases {
|
||||||
"[{}]",
|
let case = case.as_mapping().unwrap();
|
||||||
response
|
let inputs: Vec<_> = case
|
||||||
.outputs()
|
.get(&Value::from("input"))
|
||||||
.iter()
|
.unwrap()
|
||||||
.map(|output| {
|
.as_sequence()
|
||||||
match output {
|
.unwrap()
|
||||||
// Remove the `_nonce` from the record string.
|
.iter()
|
||||||
console::program::Value::Record(record) => {
|
.map(|input| console::program::Value::<Network>::from_str(input.as_str().unwrap()).unwrap())
|
||||||
let pattern = Regex::new(r"_nonce: \d+group.public").unwrap();
|
.collect();
|
||||||
pattern.replace(&record.to_string(), "").to_string()
|
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(", ")
|
||||||
})
|
),
|
||||||
.join(", ")
|
Err(err) => format!("SnarkVMError({err})"),
|
||||||
),
|
};
|
||||||
Err(err) => format!("SnarkVMError({err})"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Store the inputs and outputs in a map.
|
// Store the inputs and outputs in a map.
|
||||||
let mut result = BTreeMap::new();
|
let mut result = BTreeMap::new();
|
||||||
result.insert("input".to_string(), input_string);
|
result.insert("input".to_string(), input_string);
|
||||||
result.insert("output".to_string(), output_string);
|
result.insert("output".to_string(), output_string);
|
||||||
|
|
||||||
// Add the hashes of the inputs and outputs to the function results.
|
// Add the hashes of the inputs and outputs to the function results.
|
||||||
function_results.push(result);
|
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);
|
||||||
}
|
}
|
||||||
|
Ok(serde_yaml::to_value(outputs).expect("serialization failed"))
|
||||||
// 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"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestRunner;
|
struct TestRunner;
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// 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::{
|
use leo_errors::{
|
||||||
emitter::{Buffer, Emitter, Handler},
|
emitter::{Buffer, Emitter, Handler},
|
||||||
LeoError,
|
LeoError,
|
||||||
@ -34,6 +34,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
use leo_test_framework::test::TestConfig;
|
||||||
|
|
||||||
pub type Network = Testnet3;
|
pub type Network = Testnet3;
|
||||||
#[allow(unused)]
|
#[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>, ()> {
|
pub fn setup_build_directory(program_name: &str, bytecode: &String, handler: &Handler) -> Result<Package<Network>, ()> {
|
||||||
// Initialize a temporary directory.
|
// Initialize a temporary directory.
|
||||||
let directory = temp_dir();
|
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))
|
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/");
|
let output_dir = PathBuf::from("/tmp/output/");
|
||||||
fs::create_dir_all(output_dir.clone()).unwrap();
|
fs::create_dir_all(output_dir.clone()).unwrap();
|
||||||
|
|
||||||
@ -95,17 +112,7 @@ pub fn new_compiler(handler: &Handler, main_file_path: PathBuf) -> Compiler<'_>
|
|||||||
handler,
|
handler,
|
||||||
main_file_path,
|
main_file_path,
|
||||||
output_dir,
|
output_dir,
|
||||||
Some(CompilerOptions {
|
compiler_options
|
||||||
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,
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,8 +120,9 @@ pub fn parse_program<'a>(
|
|||||||
handler: &'a Handler,
|
handler: &'a Handler,
|
||||||
program_string: &str,
|
program_string: &str,
|
||||||
cwd: Option<PathBuf>,
|
cwd: Option<PathBuf>,
|
||||||
|
compiler_options: Option<CompilerOptions>,
|
||||||
) -> Result<Compiler<'a>, LeoError> {
|
) -> 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);
|
let name = cwd.map_or_else(|| FileName::Custom("compiler-test".into()), FileName::Real);
|
||||||
compiler.parse_program_from_string(program_string, name)?;
|
compiler.parse_program_from_string(program_string, name)?;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user