Add support for multi-file compile tests

This commit is contained in:
Pranav Gaddamadugu 2024-03-26 12:57:57 -07:00
parent 8456096ac8
commit f4af8a77d2
9 changed files with 86 additions and 46 deletions

1
Cargo.lock generated
View File

@ -1459,6 +1459,7 @@ dependencies = [
name = "leo-compiler"
version = "1.10.0"
dependencies = [
"disassembler",
"dotenvy",
"indexmap 1.9.3",
"leo-ast",

View File

@ -45,6 +45,9 @@ version = "0.10"
version = "1.9"
features = []
[dev-dependencies.disassembler]
path = "../../utils/disassembler"
[dev-dependencies.leo-test-framework]
path = "../../tests/test-framework"
@ -76,4 +79,4 @@ version = "3.10"
[features]
default = [ ]
ci_skip = [ "leo-ast/ci_skip" ]
ci_skip = [ "leo-ast/ci_skip" ]

View File

@ -27,16 +27,20 @@ use utilities::{
BufferEmitter,
};
use disassembler::disassemble_from_str;
use leo_compiler::{CompilerOptions, OutputOptions};
use leo_errors::{emitter::Handler, LeoError};
use leo_span::symbol::create_session_if_not_set_then;
use leo_test_framework::{
runner::{Namespace, ParseType, Runner},
Test,
PROGRAM_DELIMITER,
};
use snarkvm::console::prelude::*;
use indexmap::IndexMap;
use leo_span::Symbol;
use serde::{Deserialize, Serialize};
use serde_yaml::Value;
use std::{fs, path::Path, rc::Rc};
@ -101,48 +105,68 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,
},
};
// Parse the program.
let mut parsed =
handler.extend_if_error(parse_program(handler, &test.content, cwd.clone(), Some(compiler_options)))?;
// Split the test content into individual program strings based on the program delimiter.
let program_strings = test.content.split(PROGRAM_DELIMITER).collect::<Vec<&str>>();
// 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 storage for the stubs.
let mut import_stubs = IndexMap::new();
// Set up the build directory.
// Note that this function checks that the bytecode is well-formed.
let package = setup_build_directory(&program_name, &bytecode, handler)?;
// Compile each program string separately.
for program_string in program_strings {
// Parse the program.
let mut parsed = handler.extend_if_error(parse_program(
handler,
program_string,
cwd.clone(),
Some(compiler_options.clone()),
import_stubs.clone(),
))?;
// Get the program process and check all instructions.
handler.extend_if_error(package.get_process().map_err(LeoError::Anyhow))?;
// Compile the program to bytecode.
let program_name = parsed.program_name.to_string();
let full_program_name = format!("{program_name}.{}", parsed.network);
let bytecode = handler.extend_if_error(compile_and_process(&mut parsed))?;
// Hash the ast files.
let (initial_ast, unrolled_ast, ssa_ast, flattened_ast, destructured_ast, inlined_ast, dce_ast) = hash_asts();
// Add the bytecode to the import stubs.
let stub = handler.extend_if_error(disassemble_from_str(&bytecode).map_err(|err| err.into()))?;
import_stubs.insert(Symbol::intern(&program_name), stub);
// Hash the symbol tables.
let (initial_symbol_table, type_checked_symbol_table, unrolled_symbol_table) = hash_symbol_tables();
// Set up the build directory.
// Note that this function checks that the bytecode is well-formed.
let package = setup_build_directory(&full_program_name, &bytecode, handler)?;
// 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.");
// 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, destructured_ast, inlined_ast, dce_ast) =
hash_asts();
// Hash the symbol tables.
let (initial_symbol_table, type_checked_symbol_table, unrolled_symbol_table) = hash_symbol_tables();
// 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_symbol_table,
type_checked_symbol_table,
unrolled_symbol_table,
initial_ast,
unrolled_ast,
ssa_ast,
flattened_ast,
destructured_ast,
inlined_ast,
dce_ast,
bytecode: hash_content(&bytecode),
warnings: buf.1.take().to_string(),
};
outputs.push(final_output);
}
let final_output = CompileOutput {
initial_symbol_table,
type_checked_symbol_table,
unrolled_symbol_table,
initial_ast,
unrolled_ast,
ssa_ast,
flattened_ast,
destructured_ast,
inlined_ast,
dce_ast,
bytecode: hash_content(&bytecode),
warnings: buf.1.take().to_string(),
};
outputs.push(final_output);
}
Ok(serde_yaml::to_value(outputs).expect("serialization failed"))
}

View File

@ -116,8 +116,13 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
};
// Parse the program.
let mut parsed =
handler.extend_if_error(parse_program(handler, &test.content, cwd.clone(), Some(compiler_options)))?;
let mut parsed = handler.extend_if_error(parse_program(
handler,
&test.content,
cwd.clone(),
Some(compiler_options),
Default::default(),
))?;
// Compile the program to bytecode.
let program_name = format!("{}.{}", parsed.program_name, parsed.network);

View File

@ -30,7 +30,8 @@ use leo_test_framework::{test::TestConfig, Test};
use snarkvm::prelude::*;
use indexmap::IndexMap;
use leo_ast::ProgramVisitor;
use leo_ast::{ProgramVisitor, Stub};
use leo_span::Symbol;
use snarkvm::{file::Manifest, package::Package};
use std::{
cell::RefCell,
@ -139,6 +140,7 @@ pub fn new_compiler(
handler: &Handler,
main_file_path: PathBuf,
compiler_options: Option<CompilerOptions>,
import_stubs: IndexMap<Symbol, Stub>,
) -> Compiler<'_> {
let output_dir = PathBuf::from("/tmp/output/");
fs::create_dir_all(output_dir.clone()).unwrap();
@ -150,7 +152,7 @@ pub fn new_compiler(
main_file_path,
output_dir,
compiler_options,
IndexMap::new(),
import_stubs,
)
}
@ -159,8 +161,10 @@ pub fn parse_program<'a>(
program_string: &str,
cwd: Option<PathBuf>,
compiler_options: Option<CompilerOptions>,
import_stubs: IndexMap<Symbol, Stub>,
) -> Result<Compiler<'a>, LeoError> {
let mut compiler = new_compiler(handler, cwd.clone().unwrap_or_else(|| "compiler-test".into()), compiler_options);
let mut compiler =
new_compiler(handler, cwd.clone().unwrap_or_else(|| "compiler-test".into()), compiler_options, import_stubs);
let name = cwd.map_or_else(|| FileName::Custom("compiler-test".into()), FileName::Real);
compiler.parse_program_from_string(program_string, name)?;

View File

@ -56,6 +56,7 @@ version = "1.10"
[dev-dependencies.leo-compiler]
path = "../../compiler/compiler"
[dev-dependencies.leo-span]
path = "../../compiler/span"

View File

@ -25,6 +25,8 @@ use std::{
use crate::{error::*, fetch::find_tests, output::TestExpectation, test::*};
pub const PROGRAM_DELIMITER: &str = "// --- Next Program --- //";
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ParseType {
Line,

View File

@ -71,8 +71,8 @@ pub fn disassemble<N: Network, Instruction: InstructionTrait<N>, Command: Comman
}
}
pub fn disassemble_from_str(program: String) -> Result<Stub, UtilError> {
match Program::<CurrentNetwork>::from_str(&program) {
pub fn disassemble_from_str(program: &str) -> Result<Stub, UtilError> {
match Program::<CurrentNetwork>::from_str(program) {
Ok(p) => Ok(disassemble(p)),
Err(_) => Err(UtilError::snarkvm_parsing_error(Default::default())),
}
@ -109,7 +109,7 @@ mod tests {
create_session_if_not_set_then(|_| {
let program_from_file =
fs::read_to_string("../tmp/.aleo/registry/testnet3/zk_bitwise_stack_v0_0_2.aleo").unwrap();
let _program = disassemble_from_str(program_from_file).unwrap();
let _program = disassemble_from_str(&program_from_file).unwrap();
});
}
}

View File

@ -304,7 +304,7 @@ impl Retriever {
})?;
// Cache the disassembled stub
let stub: Stub = disassemble_from_str(content)?;
let stub: Stub = disassemble_from_str(&content)?;
if cur_context.add_stub(stub.clone()) {
Err(UtilError::duplicate_dependency_name_error(stub.stub_id.name.name, Default::default()))?;
}
@ -484,7 +484,7 @@ fn retrieve_from_network(
})?;
// Disassemble into Stub
let stub: Stub = disassemble_from_str(file_str)?;
let stub: Stub = disassemble_from_str(&file_str)?;
// Create entry for leo.lock
Ok((