mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-23 10:12:21 +03:00
Initial implementation of Execute namespace; fmt
This commit is contained in:
parent
789c2e6443
commit
59915ed315
@ -51,7 +51,6 @@ impl Namespace for CompileNamespace {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, PartialEq, Eq, Serialize)]
|
||||
struct CompileOutput {
|
||||
pub initial_ast: String,
|
||||
|
210
compiler/compiler/tests/execute.rs
Normal file
210
compiler/compiler/tests/execute.rs
Normal file
@ -0,0 +1,210 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// 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/>.
|
||||
|
||||
mod utilities;
|
||||
use utilities::{compile_and_process, hash_content, hash_file, parse_program, temp_dir, BufferEmitter};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
use snarkvm::console;
|
||||
use snarkvm::file::Manifest;
|
||||
use snarkvm::package::Package;
|
||||
use snarkvm::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use std::{fs, path::Path, rc::Rc};
|
||||
use std::{fs::File, io::Write};
|
||||
|
||||
type Network = Testnet3;
|
||||
pub type Aleo = snarkvm::circuit::AleoV0;
|
||||
|
||||
struct ExecuteNamespace;
|
||||
|
||||
impl Namespace for ExecuteNamespace {
|
||||
fn parse_type(&self) -> ParseType {
|
||||
ParseType::Whole
|
||||
}
|
||||
|
||||
fn run_test(&self, test: Test) -> Result<Value, String> {
|
||||
let buf = BufferEmitter(Rc::default(), Rc::default());
|
||||
let handler = Handler::new(Box::new(buf.clone()));
|
||||
|
||||
create_session_if_not_set_then(|_| run_test(test, &handler, &buf).map_err(|()| buf.0.take().to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Format this better.
|
||||
#[derive(Deserialize, PartialEq, Eq, Serialize)]
|
||||
struct ExecuteOutput {
|
||||
pub initial_ast: String,
|
||||
pub unrolled_ast: String,
|
||||
pub ssa_ast: String,
|
||||
pub flattened_ast: String,
|
||||
pub results: BTreeMap<String, Vec<(String, String)>>,
|
||||
}
|
||||
|
||||
fn run_test(test: Test, handler: &Handler, _err_buf: &BufferEmitter) -> Result<Value, ()> {
|
||||
// Check for CWD option:
|
||||
// ``` cwd: import ```
|
||||
// When set, uses different working directory for current file.
|
||||
// If not, uses file path as current working directory.
|
||||
let cwd = test.config.get("cwd").map(|val| {
|
||||
let mut cwd = test.path.clone();
|
||||
cwd.pop();
|
||||
cwd.join(val.as_str().unwrap())
|
||||
});
|
||||
|
||||
// Parse the program.
|
||||
let mut parsed = handler.extend_if_error(parse_program(handler, &test.content, cwd))?;
|
||||
|
||||
// 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))?;
|
||||
|
||||
// Extract the cases from the test config.
|
||||
let all_cases = test
|
||||
.config
|
||||
.get("cases")
|
||||
.expect("An `Execute` config must have a `cases` field.")
|
||||
.as_mapping()
|
||||
.unwrap();
|
||||
|
||||
// Initialize a map for the expected results.
|
||||
let mut results = BTreeMap::new();
|
||||
|
||||
// Run snarkvm package.
|
||||
{
|
||||
// Initialize a temporary directory.
|
||||
let directory = temp_dir();
|
||||
|
||||
// Create the program id.
|
||||
let program_id = ProgramID::<Network>::from_str(&program_name).unwrap();
|
||||
|
||||
// Write the program string to a file in the temporary directory.
|
||||
let path = directory.join("main.aleo");
|
||||
let mut file = File::create(path).unwrap();
|
||||
file.write_all(bytecode.as_bytes()).unwrap();
|
||||
|
||||
// Create the manifest file.
|
||||
let manifest_file = Manifest::create(&directory, &program_id).unwrap();
|
||||
|
||||
// Create the build directory.
|
||||
let build_directory = directory.join("build");
|
||||
std::fs::create_dir_all(build_directory).unwrap();
|
||||
|
||||
// Open the package at the temporary directory.
|
||||
let package = handler.extend_if_error(Package::<Testnet3>::open(&directory).map_err(LeoError::Anyhow))?;
|
||||
|
||||
// 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("inputs"))
|
||||
.unwrap()
|
||||
.as_sequence()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|input| console::program::Value::<Network>::from_str(input.as_str().unwrap()).unwrap())
|
||||
.collect();
|
||||
let inputs_hash = hash_content(&format!(
|
||||
"[{}]",
|
||||
inputs.iter().map(|input| input.to_string()).join(", ")
|
||||
));
|
||||
|
||||
let outputs: Vec<_> = case
|
||||
.get(&Value::from("expected"))
|
||||
.unwrap()
|
||||
.as_sequence()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|output| console::program::Value::<Network>::from_str(output.as_str().unwrap()).unwrap())
|
||||
.collect();
|
||||
let outputs_hash = hash_content(&format!(
|
||||
"[{}]",
|
||||
outputs.iter().map(|output| output.to_string()).join(", ")
|
||||
));
|
||||
|
||||
// Execute the program and get the outputs.
|
||||
let (_response, _, _, _) = handler.extend_if_error(
|
||||
package
|
||||
.run::<Aleo, _>(
|
||||
None,
|
||||
manifest_file.development_private_key(),
|
||||
function_name,
|
||||
&inputs,
|
||||
rng,
|
||||
)
|
||||
.map_err(LeoError::Anyhow),
|
||||
)?;
|
||||
|
||||
// TODO: Check that outputs match
|
||||
|
||||
// Add the hashes of the inputs and outputs to the function results.
|
||||
function_results.push((inputs_hash, outputs_hash));
|
||||
}
|
||||
results.insert(function_name.to_string(), function_results);
|
||||
}
|
||||
}
|
||||
|
||||
let initial_ast = hash_file("/tmp/output/test.initial_ast.json");
|
||||
let unrolled_ast = hash_file("/tmp/output/test.unrolled_ast.json");
|
||||
let ssa_ast = hash_file("/tmp/output/test.ssa_ast.json");
|
||||
let flattened_ast = hash_file("/tmp/output/test.flattened_ast.json");
|
||||
|
||||
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,
|
||||
results,
|
||||
};
|
||||
Ok(serde_yaml::to_value(&final_output).expect("serialization failed"))
|
||||
}
|
||||
|
||||
struct TestRunner;
|
||||
|
||||
impl Runner for TestRunner {
|
||||
fn resolve_namespace(&self, name: &str) -> Option<Box<dyn Namespace>> {
|
||||
Some(match name {
|
||||
"Execute" => Box::new(ExecuteNamespace),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn execution_tests() {
|
||||
leo_test_framework::run_tests(&TestRunner, "execution");
|
||||
}
|
29
tests/execution/chain.leo
Normal file
29
tests/execution/chain.leo
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
namespace: Execute
|
||||
expectation: Match
|
||||
cases:
|
||||
main:
|
||||
- inputs: ["1u32"]
|
||||
expected: ["true"]
|
||||
- inputs: ["2u32"]
|
||||
expected: ["true"]
|
||||
- inputs: ["3u32"]
|
||||
expected: ["true"]
|
||||
- inputs: ["4u32"]
|
||||
expected: ["false"]
|
||||
*/
|
||||
|
||||
program test.aleo {
|
||||
transition main(x: u32) -> bool {
|
||||
let c: u32 = 0u32;
|
||||
|
||||
if x == 1u32 {
|
||||
c = 1u32;
|
||||
} else if x == 2u32 {
|
||||
c = 2u32;
|
||||
} else {
|
||||
c = 3u32;
|
||||
}
|
||||
return c == x;
|
||||
}
|
||||
}
|
@ -152,6 +152,9 @@ pub fn emit_errors(
|
||||
}
|
||||
None
|
||||
}
|
||||
(Ok(Ok(output)), TestExpectationMode::Match) => {
|
||||
todo!()
|
||||
}
|
||||
(Ok(Ok(_tokens)), TestExpectationMode::Fail) => Some(TestError::PassedAndShouldntHave {
|
||||
test: test.to_string(),
|
||||
index: test_index,
|
||||
@ -161,6 +164,9 @@ pub fn emit_errors(
|
||||
error: err.to_string(),
|
||||
index: test_index,
|
||||
}),
|
||||
(Ok(Err(err)), TestExpectationMode::Match) => {
|
||||
todo!()
|
||||
}
|
||||
(Ok(Err(err)), TestExpectationMode::Fail) => {
|
||||
let expected_output: Option<String> =
|
||||
expected_output.map(|x| serde_yaml::from_value(x).expect("test expectation deserialize failed"));
|
||||
|
@ -18,9 +18,14 @@ use std::collections::BTreeMap;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug, Clone)]
|
||||
pub enum TestExpectationMode {
|
||||
/// The test should pass.
|
||||
Pass,
|
||||
/// The test should fail.
|
||||
Fail,
|
||||
/// The test should be skipped.
|
||||
Skip,
|
||||
/// The test should pass and match the expected output.
|
||||
Match,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
|
Loading…
Reference in New Issue
Block a user