extern crate diff; extern crate twasm_utils as utils; extern crate wabt; extern crate tetsy_wasm; use std::fs; use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; use tetsy_wasm::elements; fn slurp>(path: P) -> io::Result> { let mut f = fs::File::open(path)?; let mut buf = vec![]; f.read_to_end(&mut buf)?; Ok(buf) } fn dump>(path: P, buf: &[u8]) -> io::Result<()> { let mut f = fs::File::create(path)?; f.write_all(buf)?; Ok(()) } fn validate_wasm(binary: &[u8]) -> Result<(), wabt::Error> { wabt::Module::read_binary( &binary, &Default::default() )?.validate()?; Ok(()) } fn run_diff_test Vec>(test_dir: &str, name: &str, test: F) { // FIXME: not going to work on windows? let mut fixture_path = PathBuf::from(concat!( env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/", )); fixture_path.push(test_dir); fixture_path.push(name); // FIXME: not going to work on windows? let mut expected_path = PathBuf::from(concat!( env!("CARGO_MANIFEST_DIR"), "/tests/expectations/" )); expected_path.push(test_dir); expected_path.push(name); let fixture_wat = slurp(&fixture_path).expect("Failed to read fixture"); let fixture_wasm = wabt::wat2wasm(fixture_wat).expect("Failed to read fixture"); validate_wasm(&fixture_wasm).expect("Fixture is invalid"); let expected_wat = slurp(&expected_path).unwrap_or_default(); let expected_wat = String::from_utf8_lossy(&expected_wat); let actual_wasm = test(fixture_wasm.as_ref()); validate_wasm(&actual_wasm).expect("Result module is invalid"); let actual_wat = wabt::wasm2wat(&actual_wasm).expect("Failed to convert result wasm to wat"); if actual_wat != expected_wat { println!("difference!"); println!("--- {}", expected_path.display()); println!("+++ {} test {}", test_dir, name); for diff in diff::lines(&expected_wat, &actual_wat) { match diff { diff::Result::Left(l) => println!("-{}", l), diff::Result::Both(l, _) => println!(" {}", l), diff::Result::Right(r) => println!("+{}", r), } } dump(&expected_path, actual_wat.as_bytes()).expect("Failed to write to expected"); panic!(); } } mod stack_height { use super::*; macro_rules! def_stack_height_test { ( $name:ident ) => { #[test] fn $name() { run_diff_test("stack-height", concat!(stringify!($name), ".wat"), |input| { let module = elements::deserialize_buffer(input).expect("Failed to deserialize"); let instrumented = utils::stack_height::inject_limiter(module, 1024).expect("Failed to instrument with stack counter"); elements::serialize(instrumented).expect("Failed to serialize") }); } }; } def_stack_height_test!(simple); def_stack_height_test!(start); def_stack_height_test!(table); def_stack_height_test!(global); def_stack_height_test!(imports); } mod gas { use super::*; macro_rules! def_gas_test { ( $name:ident ) => { #[test] fn $name() { run_diff_test("gas", concat!(stringify!($name), ".wat"), |input| { let rules = utils::rules::Set::default(); let module = elements::deserialize_buffer(input).expect("Failed to deserialize"); let instrumented = utils::inject_gas_counter(module, &rules, "env") .expect("Failed to instrument with gas metering"); elements::serialize(instrumented).expect("Failed to serialize") }); } }; } def_gas_test!(ifs); def_gas_test!(simple); def_gas_test!(start); def_gas_test!(call); def_gas_test!(branch); }