#![cfg(any(cargo_difftests, docsrs))]
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use cargo_difftests_core::CoreTestDesc;
mod llvm_profiling;
pub struct TestDesc<T: serde::Serialize> {
pub bin_path: PathBuf,
pub extra: T,
}
struct SelfProfileWriter {
llvm_profile_self_file: PathBuf,
}
impl SelfProfileWriter {
fn do_write_to_file(file: &std::path::Path) {
unsafe {
#[allow(temporary_cstring_as_ptr)]
llvm_profiling::__llvm_profile_set_filename(
std::ffi::CString::new(file.to_str().unwrap())
.unwrap()
.as_ptr(),
);
let r = llvm_profiling::__llvm_profile_write_file();
assert_eq!(r, 0);
}
}
}
impl Drop for SelfProfileWriter {
fn drop(&mut self) {
Self::do_write_to_file(&self.llvm_profile_self_file);
}
}
enum DifftestsEnvInner {
Test {
#[allow(dead_code)]
self_profile_drop_writer: SelfProfileWriter,
#[cfg(all(
feature = "enforce-single-running-test",
not(feature = "parallel-groups")
))]
_t_lock: std::sync::MutexGuard<'static, ()>,
},
#[cfg(feature = "groups")]
Group(groups::GroupDifftestsEnv),
}
#[cfg(feature = "parallel-groups")]
impl Drop for DifftestsEnvInner {
fn drop(&mut self) {
match self {
DifftestsEnvInner::Test { .. } => {
let mut _l = groups::wr_test_group_dec();
*_l = groups::State::None;
groups::crs_notify();
}
DifftestsEnvInner::Group(_) => {}
}
}
}
pub struct DifftestsEnv {
llvm_profile_file_name: OsString,
llvm_profile_file_value: OsString,
#[allow(dead_code)]
difftests_env_inner: DifftestsEnvInner,
}
#[cfg(all(
feature = "enforce-single-running-test",
not(feature = "parallel-groups")
))]
fn test_lock() -> std::sync::MutexGuard<'static, ()> {
use std::sync::{Mutex, OnceLock};
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
let lock = LOCK.get_or_init(|| Mutex::new(()));
lock.lock().unwrap()
}
impl DifftestsEnv {
pub fn env_for_children(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> {
std::iter::once((
self.llvm_profile_file_name.as_os_str(),
self.llvm_profile_file_value.as_os_str(),
))
}
}
pub fn init<T: serde::Serialize>(
desc: TestDesc<T>,
tmpdir: &Path,
) -> std::io::Result<DifftestsEnv> {
#[cfg(all(
feature = "enforce-single-running-test",
not(feature = "parallel-groups")
))]
let _t_lock = test_lock();
#[cfg(feature = "parallel-groups")]
groups::wr_test_group_inc(None);
if tmpdir.exists() {
std::fs::remove_dir_all(tmpdir)?;
}
std::fs::create_dir_all(tmpdir)?;
let self_profile_file =
tmpdir.join(cargo_difftests_core::CARGO_DIFFTESTS_SELF_PROFILE_FILENAME);
std::fs::write(&self_profile_file, "")?;
let self_info_path = tmpdir.join(cargo_difftests_core::CARGO_DIFFTESTS_SELF_JSON_FILENAME);
let core_test_desc = CoreTestDesc {
bin_path: desc.bin_path,
extra: serde_json::to_value(&desc.extra).unwrap(),
};
let self_info = serde_json::to_string(&core_test_desc).unwrap();
std::fs::write(self_info_path, self_info)?;
std::fs::write(
tmpdir.join(cargo_difftests_core::CARGO_DIFFTESTS_VERSION_FILENAME),
env!("CARGO_PKG_VERSION"),
)?;
let profraw_path =
tmpdir.join(cargo_difftests_core::CARGO_DIFFTESTS_OTHER_PROFILE_FILENAME_TEMPLATE);
let r = Ok(DifftestsEnv {
llvm_profile_file_name: "LLVM_PROFILE_FILE".into(),
llvm_profile_file_value: profraw_path.into(),
difftests_env_inner: DifftestsEnvInner::Test {
self_profile_drop_writer: SelfProfileWriter {
llvm_profile_self_file: self_profile_file,
},
#[cfg(all(
feature = "enforce-single-running-test",
not(feature = "parallel-groups")
))]
_t_lock,
},
});
unsafe {
llvm_profiling::__llvm_profile_reset_counters();
}
r
}
pub mod groups;