diff --git a/compiler/compiler/tests/compile.rs b/compiler/compiler/tests/compile.rs
index 1078c76623..2ab7377806 100644
--- a/compiler/compiler/tests/compile.rs
+++ b/compiler/compiler/tests/compile.rs
@@ -51,7 +51,6 @@ impl Namespace for CompileNamespace {
}
}
-
#[derive(Deserialize, PartialEq, Eq, Serialize)]
struct CompileOutput {
pub initial_ast: String,
diff --git a/compiler/compiler/tests/execute.rs b/compiler/compiler/tests/execute.rs
new file mode 100644
index 0000000000..190b9d0592
--- /dev/null
+++ b/compiler/compiler/tests/execute.rs
@@ -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 .
+
+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 {
+ 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>,
+}
+
+fn run_test(test: Test, handler: &Handler, _err_buf: &BufferEmitter) -> Result {
+ // 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::::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::::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::::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::::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::(
+ 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> {
+ Some(match name {
+ "Execute" => Box::new(ExecuteNamespace),
+ _ => return None,
+ })
+ }
+}
+
+#[test]
+pub fn execution_tests() {
+ leo_test_framework::run_tests(&TestRunner, "execution");
+}
diff --git a/tests/execution/chain.leo b/tests/execution/chain.leo
new file mode 100644
index 0000000000..40b922a331
--- /dev/null
+++ b/tests/execution/chain.leo
@@ -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;
+ }
+}
diff --git a/tests/test-framework/src/error.rs b/tests/test-framework/src/error.rs
index 81880e631c..1373e4e361 100644
--- a/tests/test-framework/src/error.rs
+++ b/tests/test-framework/src/error.rs
@@ -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 =
expected_output.map(|x| serde_yaml::from_value(x).expect("test expectation deserialize failed"));
diff --git a/tests/test-framework/src/test.rs b/tests/test-framework/src/test.rs
index c965be41f5..19d43a0c7d 100644
--- a/tests/test-framework/src/test.rs
+++ b/tests/test-framework/src/test.rs
@@ -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)]