mod utils;

use crate::utils::StdioCapturer;
use anyhow::anyhow;
use wasmer::compiler::{compile_with, compiler_for_backend};
use wasmer_emscripten::{generate_emscripten_env, run_emscripten_instance, EmscriptenGlobals};

use lazy_static::lazy_static;
use std::sync::Mutex;
lazy_static! {
    // We want to run emscripten tests one by one
    // Based from: https://stackoverflow.com/questions/51694017/how-can-i-avoid-running-some-tests-in-parallel
    static ref EMSCRIPTEN_LOCK: Mutex<()> = Mutex::new(());
}

// The generated tests (from build.rs) look like:
// #[cfg(test)]
// mod singlepass {
//     mod wasi {
//         #[test]
//         fn test_hello_world() -> anyhow::Result<()> {
//             crate::run_wasi(
//                 "tests/emscripten_resources/emtests/test_hello_world.wasm",
//                 "tests/emscripten_resources/emtests/test_hello_world.out",
//                 "singlepass"
//             )
//         }
//     }
// }
include!(concat!(env!("OUT_DIR"), "/generated_emtests.rs"));

fn run_emscripten(wasm_program_path: &str, output_path: &str, backend: &str) -> anyhow::Result<()> {
    let _shared = EMSCRIPTEN_LOCK.lock().unwrap_or_else(|e| e.into_inner());

    let backend = utils::get_backend_from_str(backend)?;
    let program_name = "name";

    let wasm_binary = std::fs::read(wasm_program_path)?;
    let compiler = compiler_for_backend(backend).expect("Backend not recognized");
    let module = compile_with(&wasm_binary, &*compiler).unwrap();

    let mut emscripten_globals = EmscriptenGlobals::new(&module).expect("globals are valid");
    let import_object = generate_emscripten_env(&mut emscripten_globals);

    let mut instance = module
        .instantiate(&import_object)
        .map_err(|err| anyhow!("Can't instantiate the WebAssembly module: {:?}", err))?;

    let capturer = StdioCapturer::new();

    run_emscripten_instance(
        &module,
        &mut instance,
        &mut emscripten_globals,
        program_name,
        vec![],
        None,
        vec![],
    )
    .expect("run_emscripten_instance finishes");

    let output = capturer.end().unwrap().0;

    let expected_output = String::from_utf8(std::fs::read(output_path)?)?;

    assert!(
        output.contains(&expected_output),
        "Output: `{}` does not contain expected output: `{}`",
        output,
        expected_output
    );
    Ok(())
}