122 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
use pallas_primitives::conway::Language;
 | 
						|
use std::{
 | 
						|
    ffi::OsStr,
 | 
						|
    fs,
 | 
						|
    path::{Path, PathBuf},
 | 
						|
};
 | 
						|
use uplc::{
 | 
						|
    ast::{Name, NamedDeBruijn, Program},
 | 
						|
    machine::cost_model::ExBudget,
 | 
						|
    parser,
 | 
						|
};
 | 
						|
use walkdir::WalkDir;
 | 
						|
 | 
						|
const PARSE_ERROR: &str = "parse error";
 | 
						|
const EVALUATION_FAILURE: &str = "evaluation failure";
 | 
						|
 | 
						|
fn expected_to_program(expected_file: &PathBuf) -> Result<Program<Name>, String> {
 | 
						|
    let code = fs::read_to_string(expected_file).expect("Failed to read .uplc.expected file");
 | 
						|
 | 
						|
    if code.contains(PARSE_ERROR) {
 | 
						|
        Err(PARSE_ERROR.to_string())
 | 
						|
    } else if code.contains(EVALUATION_FAILURE) {
 | 
						|
        Err(EVALUATION_FAILURE.to_string())
 | 
						|
    } else {
 | 
						|
        parser::program(&code).map_err(|_| code)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn file_to_budget(expected_budget_file: &PathBuf) -> Result<ExBudget, String> {
 | 
						|
    fs::read_to_string(expected_budget_file)
 | 
						|
        .map_err(|e| format!("failed to read .uplc.budget.expected file: {e:?}"))
 | 
						|
        .and_then(|src| budget::ex_budget(&src).map_err(|e| format!("{e:?}")))
 | 
						|
}
 | 
						|
 | 
						|
peg::parser! {
 | 
						|
    grammar budget() for str {
 | 
						|
        pub rule ex_budget() -> ExBudget
 | 
						|
          = "({" _* "cpu" _* ":" _* cpu:decimal() _* "|" _* "mem" _* ":" _* mem:decimal() _* "})" _* {
 | 
						|
              ExBudget { cpu, mem }
 | 
						|
          }
 | 
						|
 | 
						|
        rule decimal() -> i64
 | 
						|
          = n:$(['0'..='9']+) {? n.parse().or(Err("decimal")) }
 | 
						|
 | 
						|
        rule _ = [' ' | '\n' | '\r' | '\t'] / "--" $([^ '\n']*) "\n"
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn actual_evaluation_result(
 | 
						|
    file: &Path,
 | 
						|
    language: &Language,
 | 
						|
) -> Result<(Program<NamedDeBruijn>, ExBudget), String> {
 | 
						|
    let code = fs::read_to_string(file).expect("Failed to read .uplc file");
 | 
						|
 | 
						|
    let program = parser::program(&code).map_err(|_| PARSE_ERROR.to_string())?;
 | 
						|
 | 
						|
    let program: Program<NamedDeBruijn> = program
 | 
						|
        .try_into()
 | 
						|
        .map_err(|_| EVALUATION_FAILURE.to_string())?;
 | 
						|
 | 
						|
    let version = program.version;
 | 
						|
 | 
						|
    let eval = program.eval_version(Default::default(), language);
 | 
						|
 | 
						|
    let cost = eval.cost();
 | 
						|
 | 
						|
    let term = eval.result().map_err(|_| EVALUATION_FAILURE.to_string())?;
 | 
						|
 | 
						|
    let program = Program { version, term };
 | 
						|
 | 
						|
    Ok((program, cost))
 | 
						|
}
 | 
						|
 | 
						|
fn plutus_conformance_tests(language: Language) {
 | 
						|
    let root = format!(
 | 
						|
        "test_data/conformance/{}",
 | 
						|
        match language {
 | 
						|
            Language::PlutusV1 => "v1",
 | 
						|
            Language::PlutusV2 => "v2",
 | 
						|
            Language::PlutusV3 => "v3",
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
    for entry in WalkDir::new(root).into_iter().filter_map(|e| e.ok()) {
 | 
						|
        let path = entry.path();
 | 
						|
 | 
						|
        if path.extension().and_then(OsStr::to_str) == Some("uplc") {
 | 
						|
            let expected_file = path.with_extension("uplc.expected");
 | 
						|
            let expected_budget_file = path.with_extension("uplc.budget.expected");
 | 
						|
 | 
						|
            let eval = actual_evaluation_result(path, &language);
 | 
						|
            let expected = expected_to_program(&expected_file)
 | 
						|
                .map(|program| Program::<NamedDeBruijn>::try_from(program).unwrap());
 | 
						|
 | 
						|
            match eval {
 | 
						|
                Ok((actual, cost)) => {
 | 
						|
                    pretty_assertions::assert_eq!(expected, Ok(actual), "{}", path.display());
 | 
						|
                    match language {
 | 
						|
                        Language::PlutusV1 | Language::PlutusV2 => {}
 | 
						|
                        Language::PlutusV3 => {
 | 
						|
                            if let Ok(budget) = file_to_budget(&expected_budget_file) {
 | 
						|
                                pretty_assertions::assert_eq!(budget, cost, "{}", path.display());
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                Err(err) => pretty_assertions::assert_eq!(expected, Err(err), "{}", path.display()),
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn plutus_conformance_tests_v2() {
 | 
						|
    plutus_conformance_tests(Language::PlutusV2)
 | 
						|
}
 | 
						|
 | 
						|
#[test]
 | 
						|
fn plutus_conformance_tests_v3() {
 | 
						|
    plutus_conformance_tests(Language::PlutusV3)
 | 
						|
}
 |