use std::{ fs::write, io::Write, path::PathBuf, process::{Command, ExitStatus, Stdio}, hash::{Hash, Hasher}, collections::hash_map::DefaultHasher, }; use tempfile::*; pub fn run_program(source: &str, stdin: &str) -> (String, String) { let (status, stdout, stderr) = run_program_with_status(source, stdin); if !status.success() { panic!( "failed to run test program:\n--- program stdout ---\n{}\n--- program stderr ---\n{}", stdout, stderr ); } (stdout, stderr) } pub fn run_program_with_status(source: &str, stdin: &str) -> (ExitStatus, String, String) { let temp_dir = tempdir().unwrap(); let mut source_path = temp_dir.path().to_path_buf(); source_path.push("test.rs"); write(&source_path, source).unwrap(); let mut spork_out_path = temp_dir.path().to_path_buf(); spork_out_path.push("test"); // let output = Command::new("cargo") // .env("RUSTFLAGS", "-C instrument-coverage") // .env("LLVM_PROFILE_FILE", "/home/konnor/data/spork/cov/%p_%m.profraw") // .arg("build").output().unwrap(); // if !output.status.success() { // panic!( // "failed to run rustc on spork:\n--- rustc stdout ---\n{}\n--- rustc stderr ---\n{}", // String::from_utf8(output.stdout).unwrap(), // String::from_utf8(output.stderr).unwrap() // ); // } let mut state = DefaultHasher::new(); source.hash(&mut state); let metadata = base64::encode(state.finish().to_le_bytes()).replace('/', "_").replace('=', "").replace('+', "_"); let mut spork_out_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); spork_out_path.push("target"); spork_out_path.push("debug"); spork_out_path.push("deps"); spork_out_path.push("libspork-9ed0fa64698ca313.rlib"); let mut deps_out_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); deps_out_path.push("target"); deps_out_path.push("debug"); deps_out_path.push("deps"); let mut rustc_out_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); rustc_out_path.push("target"); rustc_out_path.push("debug"); rustc_out_path.push("inner_tests"); let _ = std::fs::create_dir(&rustc_out_path); let mut out_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); out_path.push("target"); out_path.push("debug"); out_path.push("inner_tests"); out_path.push(format!("test-{}", metadata)); // let mut out_path = temp_dir.path().to_path_buf(); // out_path.push("test"); let output = Command::new("rustc") .args(["--crate-name", "test"]) .arg("--edition=2021") .arg(&source_path) .args(["--crate-type", "bin"]) .arg("--emit=dep-info,link") .args(["-C", "embed-bitcode=no"]) .args(["-C", "debuginfo=2"]) // .arg("-o") // .arg(&out_path) .arg("--out-dir") .arg(&rustc_out_path) .arg("--extern") .arg(format!("spork={}", spork_out_path.to_string_lossy())) .arg("-L") .arg(deps_out_path) .args(["-C", "instrument-coverage"]) .args(["-C", &format!("metadata={}", metadata)]) .args(["-C", &format!("extra-filename=-{}", metadata)]) /* * --crate-name demo * --edition=2021 * examples/demo.rs * --error-format=json * --json=diagnostic-rendered-ansi,artifacts,future-incompat * --crate-type bin * --emit=dep-info,link * -C embed-bitcode=no * -C debuginfo=2 * -C metadata=e6004ce28faa17e4 * -C extra-filename=-e6004ce28faa17e4 * --out-dir /home/konnor/data/spork/target/debug/examples * -C incremental=/home/konnor/data/spork/target/debug/incremental * -L dependency=/home/konnor/data/spork/target/debug/deps * --extern base64=/home/konnor/data/spork/target/debug/deps/libbase64-2e31a58915f3d31d.rlib * --extern ipc_channel=/home/konnor/data/spork/target/debug/deps/libipc_channel-7780f4d05cce5a6b.rlib * --extern once_cell=/home/konnor/data/spork/target/debug/deps/libonce_cell-eab76c691b3370a2.rlib * --extern rand=/home/konnor/data/spork/target/debug/deps/librand-aebdfee6719a7431.rlib * --extern rmp_serde=/home/konnor/data/spork/target/debug/deps/librmp_serde-313d68f78405373e.rlib * --extern serde=/home/konnor/data/spork/target/debug/deps/libserde-4d5df548af5106e4.rlib * --extern spork=/home/konnor/data/spork/target/debug/deps/libspork-9ed0fa64698ca313.rlib * --extern static_assertions=/home/konnor/data/spork/target/debug/deps/libstatic_assertions-d6ca809a3bb650cc.rlib * --extern tempfile=/home/konnor/data/spork/target/debug/deps/libtempfile-d25c47d88aba1abc.rlib * --extern thiserror=/home/konnor/data/spork/target/debug/deps/libthiserror-c2afbb8194daf5ff.rlib * -C instrument-coverage */ .output() .unwrap(); if !output.status.success() { panic!( "failed to run rustc:\n--- rustc stdout ---\n{}\n--- rustc stderr ---\n{}", String::from_utf8(output.stdout).unwrap(), String::from_utf8(output.stderr).unwrap() ); } let mut child = Command::new(out_path) .env("LLVM_PROFILE_FILE", "/home/konnor/data/spork/cov/test_%p_%m.profraw") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .unwrap(); write!(child.stdin.take().unwrap(), "{}", stdin).unwrap(); let output = child.wait_with_output().unwrap(); drop(temp_dir); ( output.status, String::from_utf8(output.stdout).unwrap(), String::from_utf8(output.stderr).unwrap(), ) } pub fn should_fail_compile(source: &str) -> (String, String) { let temp_dir = tempdir().unwrap(); let mut source_path = temp_dir.path().to_path_buf(); source_path.push("test.rs"); write(&source_path, source).unwrap(); let mut spork_out_path = temp_dir.path().to_path_buf(); spork_out_path.push("test"); let output = Command::new("cargo").arg("build").output().unwrap(); if !output.status.success() { panic!( "failed to run rustc on spork:\n--- rustc stdout ---\n{}\n--- rustc stderr ---\n{}", String::from_utf8(output.stdout).unwrap(), String::from_utf8(output.stderr).unwrap() ); } let mut spork_out_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); spork_out_path.push("target"); spork_out_path.push("debug"); spork_out_path.push("libspork.rlib"); let mut deps_out_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); deps_out_path.push("target"); deps_out_path.push("debug"); deps_out_path.push("deps"); let mut out_path = temp_dir.path().to_path_buf(); out_path.push("test"); let output = Command::new("rustc") .arg(&source_path) .arg("-o") .arg(&out_path) .arg("--extern") .arg(format!("spork={}", spork_out_path.to_string_lossy())) .arg("-L") .arg(deps_out_path) .arg("--edition") .arg("2021") .output() .unwrap(); if output.status.success() { panic!( "ran rustc without fail:\n--- rustc stdout ---\n{}\n--- rustc stderr ---\n{}", String::from_utf8(output.stdout).unwrap(), String::from_utf8(output.stderr).unwrap() ); } drop(temp_dir); ( String::from_utf8(output.stdout).unwrap(), String::from_utf8(output.stderr).unwrap(), ) } macro_rules! program { ({#![allow(compile_fail)] $($token:tt)*}) => {{ stringify!($($token)*) }}; ({$($token:tt)*}) => {{ mod program { $($token)* } let _ = || { program::main() }; stringify!($($token)*) }}; } pub(crate) use program;