mirror of
https://github.com/AleoHQ/leo.git
synced 2024-08-16 18:30:37 +03:00
add test syntax and cli command
This commit is contained in:
parent
13e113d100
commit
accfff46e0
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
constraints::{generate_constraints, ConstrainedValue},
|
||||
constraints::{generate_constraints, generate_test_constraints, ConstrainedValue},
|
||||
errors::CompilerError,
|
||||
GroupType, InputValue, Program,
|
||||
};
|
||||
@ -10,7 +10,7 @@ use crate::{
|
||||
use snarkos_errors::gadgets::SynthesisError;
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::{ConstraintSynthesizer, ConstraintSystem},
|
||||
gadgets::r1cs::{ConstraintSynthesizer, ConstraintSystem, TestConstraintSystem},
|
||||
};
|
||||
|
||||
use from_pest::FromPest;
|
||||
@ -68,6 +68,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
|
||||
generate_constraints(cs, self.program, self.program_inputs)
|
||||
}
|
||||
|
||||
pub fn compile_test_constraints(
|
||||
self,
|
||||
cs: &mut TestConstraintSystem<F>,
|
||||
) -> Result<(), CompilerError> {
|
||||
generate_test_constraints::<F, G>(cs, self.program)
|
||||
}
|
||||
|
||||
// pub fn compile(&self) -> Result<ast::File, CompilerError> {
|
||||
// // Read in the main file as string
|
||||
// let unparsed_file = fs::read_to_string(&self.main_file_path).map_err(|_| CompilerError::FileReadError(self.main_file_path.clone()))?;
|
||||
|
@ -38,7 +38,7 @@ use crate::{
|
||||
|
||||
use snarkos_models::{
|
||||
curves::{Field, PrimeField},
|
||||
gadgets::r1cs::ConstraintSystem,
|
||||
gadgets::r1cs::{ConstraintSystem, TestConstraintSystem},
|
||||
};
|
||||
|
||||
pub fn generate_constraints<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>>(
|
||||
@ -66,3 +66,40 @@ pub fn generate_constraints<F: Field + PrimeField, G: GroupType<F>, CS: Constrai
|
||||
_ => Err(CompilerError::NoMainFunction),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_test_constraints<F: Field + PrimeField, G: GroupType<F>>(
|
||||
cs: &mut TestConstraintSystem<F>,
|
||||
program: Program,
|
||||
) -> Result<(), CompilerError> {
|
||||
let mut resolved_program = ConstrainedProgram::<F, G, TestConstraintSystem<F>>::new();
|
||||
let program_name = program.get_name();
|
||||
|
||||
let tests = program.tests.clone();
|
||||
|
||||
resolved_program.resolve_definitions(cs, program)?;
|
||||
|
||||
log::info!("Running {} tests", tests.len());
|
||||
|
||||
for (test_name, test_function) in tests.into_iter() {
|
||||
let full_test_name = format!("{}::{}", program_name.clone(), test_name.to_string());
|
||||
|
||||
let result = resolved_program.enforce_main_function(
|
||||
cs,
|
||||
program_name.clone(),
|
||||
test_function.0,
|
||||
vec![], // test functions should not take any inputs
|
||||
);
|
||||
|
||||
if result.is_ok() {
|
||||
log::info!(
|
||||
"test {} passed. Constraint system satisfied: {}",
|
||||
full_test_name,
|
||||
cs.is_satisfied()
|
||||
);
|
||||
} else {
|
||||
log::error!("test {} errored: {}", full_test_name, result.unwrap_err());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -414,14 +414,16 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
|
||||
self.enforce_boolean_eq(cs, bool_1, bool_2)?
|
||||
}
|
||||
(ConstrainedValue::Integer(num_1), ConstrainedValue::Integer(num_2)) => {
|
||||
num_1.enforce_equal(cs, &num_2)?
|
||||
}
|
||||
(ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => {
|
||||
fe_1.enforce_equal(cs, &fe_2)?
|
||||
}
|
||||
(ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => {
|
||||
ge_1.enforce_equal(cs, &ge_2)?
|
||||
num_1.enforce_equal(cs, &num_2).map_err(|_| {
|
||||
StatementError::AssertionFailed(num_1.to_string(), num_2.to_string())
|
||||
})?
|
||||
}
|
||||
(ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => fe_1
|
||||
.enforce_equal(cs, &fe_2)
|
||||
.map_err(|_| StatementError::AssertionFailed(fe_1.to_string(), fe_2.to_string()))?,
|
||||
(ConstrainedValue::Group(ge_1), ConstrainedValue::Group(ge_2)) => ge_1
|
||||
.enforce_equal(cs, &ge_2)
|
||||
.map_err(|_| StatementError::AssertionFailed(ge_1.to_string(), ge_2.to_string()))?,
|
||||
(ConstrainedValue::Array(arr_1), ConstrainedValue::Array(arr_2)) => {
|
||||
for (left, right) in arr_1.into_iter().zip(arr_2.into_iter()) {
|
||||
self.enforce_assert_eq_statement(cs, left, right)?;
|
||||
|
@ -34,6 +34,9 @@ pub enum StatementError {
|
||||
#[error("Cannot assert equality between {} == {}", _0, _1)]
|
||||
AssertEq(String, String),
|
||||
|
||||
#[error("Assertion {:?} == {:?} failed", _0, _1)]
|
||||
AssertionFailed(String, String),
|
||||
|
||||
#[error("If, else conditional must resolve to a boolean, got {}", _0)]
|
||||
IfElseConditional(String),
|
||||
|
||||
|
@ -287,3 +287,9 @@ impl<F: Field + PrimeField> ToBytesGadget<F> for FieldType<F> {
|
||||
self_gadget.to_bytes_strict(cs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field + PrimeField> std::fmt::Display for FieldType<F> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.get_value().ok_or(std::fmt::Error))
|
||||
}
|
||||
}
|
||||
|
@ -304,3 +304,12 @@ impl ToBytesGadget<Fq> for EdwardsGroupType {
|
||||
self_gadget.to_bytes_strict(cs)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EdwardsGroupType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
EdwardsGroupType::Constant(constant) => write!(f, "{:?}", constant),
|
||||
EdwardsGroupType::Allocated(allocated) => write!(f, "{:?}", allocated),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use snarkos_models::{
|
||||
},
|
||||
},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
pub mod edwards_bls12;
|
||||
|
||||
@ -22,6 +22,7 @@ pub trait GroupType<F: Field>:
|
||||
Sized
|
||||
+ Clone
|
||||
+ Debug
|
||||
+ Display
|
||||
+ EqGadget<F>
|
||||
+ ConditionalEqGadget<F>
|
||||
+ AllocGadget<String, F>
|
||||
|
@ -25,5 +25,8 @@ pub use self::run::*;
|
||||
pub mod setup;
|
||||
pub use self::setup::*;
|
||||
|
||||
pub mod test;
|
||||
pub use self::test::*;
|
||||
|
||||
pub mod unload;
|
||||
pub use self::unload::*;
|
||||
|
74
leo/commands/test.rs
Normal file
74
leo/commands/test.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use crate::directories::source::SOURCE_DIRECTORY_NAME;
|
||||
use crate::errors::{CLIError, TestError};
|
||||
use crate::files::{MainFile, Manifest, MAIN_FILE_NAME};
|
||||
use crate::{cli::*, cli_types::*};
|
||||
use leo_compiler::compiler::Compiler;
|
||||
use leo_compiler::group::edwards_bls12::EdwardsGroupType;
|
||||
|
||||
use snarkos_curves::edwards_bls12::Fq;
|
||||
use snarkos_models::gadgets::r1cs::TestConstraintSystem;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use std::convert::TryFrom;
|
||||
use std::env::current_dir;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestCommand;
|
||||
|
||||
impl CLI for TestCommand {
|
||||
type Options = ();
|
||||
type Output = ();
|
||||
|
||||
const NAME: NameType = "test";
|
||||
const ABOUT: AboutType = "Compile and run all tests in the current package";
|
||||
const ARGUMENTS: &'static [ArgumentType] = &[];
|
||||
const FLAGS: &'static [FlagType] = &[];
|
||||
const OPTIONS: &'static [OptionType] = &[];
|
||||
const SUBCOMMANDS: &'static [SubCommandType] = &[];
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn parse(_arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn output(_options: Self::Options) -> Result<Self::Output, CLIError> {
|
||||
let path = current_dir()?;
|
||||
|
||||
// Get the package name
|
||||
let manifest = Manifest::try_from(&path)?;
|
||||
let package_name = manifest.get_package_name();
|
||||
|
||||
// Sanitize the package path to the root directory
|
||||
let mut package_path = path.clone();
|
||||
if package_path.is_file() {
|
||||
package_path.pop();
|
||||
}
|
||||
|
||||
// Verify the main file exists
|
||||
if !MainFile::exists_at(&package_path) {
|
||||
return Err(
|
||||
TestError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into(),
|
||||
);
|
||||
}
|
||||
|
||||
// Construct the path to the main file in the source directory
|
||||
let mut main_file_path = package_path.clone();
|
||||
main_file_path.push(SOURCE_DIRECTORY_NAME);
|
||||
main_file_path.push(MAIN_FILE_NAME);
|
||||
|
||||
// Compute the current program checksum
|
||||
let program =
|
||||
Compiler::<Fq, EdwardsGroupType>::init(package_name.clone(), main_file_path.clone())?;
|
||||
|
||||
// Generate the program on the constraint system and verify correctness
|
||||
{
|
||||
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
let temporary_program = program.clone();
|
||||
let output = temporary_program.compile_test_constraints(&mut cs)?;
|
||||
log::debug!("Compiled constraints - {:#?}", output);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -44,6 +44,9 @@ pub enum CLIError {
|
||||
#[error("{}", _0)]
|
||||
SourceDirectoryError(#[from] SourceDirectoryError),
|
||||
|
||||
#[error("{}", _0)]
|
||||
TestError(#[from] TestError),
|
||||
|
||||
#[error("{}", _0)]
|
||||
VerificationKeyFileError(#[from] VerificationKeyFileError),
|
||||
}
|
||||
|
@ -9,3 +9,6 @@ pub use self::new::*;
|
||||
|
||||
pub mod run;
|
||||
pub use self::run::*;
|
||||
|
||||
pub mod test;
|
||||
pub use self::test::*;
|
||||
|
12
leo/errors/commands/test.rs
Normal file
12
leo/errors/commands/test.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::errors::ManifestError;
|
||||
|
||||
use std::ffi::OsString;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TestError {
|
||||
#[error("main file {:?} does not exist", _0)]
|
||||
MainFileDoesNotExist(OsString),
|
||||
|
||||
#[error("{}", _0)]
|
||||
ManifestError(#[from] ManifestError),
|
||||
}
|
@ -28,6 +28,7 @@ fn main() -> Result<(), CLIError> {
|
||||
RunCommand::new().display_order(7),
|
||||
PublishCommand::new().display_order(8),
|
||||
DeployCommand::new().display_order(9),
|
||||
TestCommand::new().display_order(10),
|
||||
])
|
||||
.set_term_width(0)
|
||||
.get_matches();
|
||||
@ -52,6 +53,10 @@ fn main() -> Result<(), CLIError> {
|
||||
("run", Some(arguments)) => RunCommand::output(RunCommand::parse(arguments)?),
|
||||
("publish", Some(arguments)) => PublishCommand::output(PublishCommand::parse(arguments)?),
|
||||
("deploy", Some(arguments)) => DeployCommand::output(DeployCommand::parse(arguments)?),
|
||||
("test", Some(arguments)) => {
|
||||
TestCommand::output(TestCommand::parse(arguments)?)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user