#![allow(clippy::result_large_err)]
use anyhow::{Context, Result};
use assert_cmd::assert::OutputAssertExt;
use chrono::Local;
use fs_err as fs;
use log::trace;
use std::convert::Infallible;
use std::ffi::OsString;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::LazyLock;
pub static CRATE_DIR: LazyLock<PathBuf> =
LazyLock::new(|| Path::new(file!()).parent().unwrap().join("../test-crate"));
pub static CARGO: LazyLock<OsString> = LazyLock::new(|| std::env::var_os("CARGO").unwrap());
pub static SCCACHE_BIN: LazyLock<&Path> =
LazyLock::new(|| Path::new(env!("CARGO_BIN_EXE_sccache")));
static LOGGER: LazyLock<Result<(), Infallible>> = LazyLock::new(|| {
env_logger::Builder::new()
.format(|f, record| {
writeln!(
f,
"{} [{}] - {}",
Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
record.level(),
record.args()
)
})
.parse_env("RUST_LOG")
.init();
Ok(())
});
pub struct SccacheTest<'a> {
#[allow(dead_code)]
pub tempdir: tempfile::TempDir,
pub env: Vec<(&'a str, std::ffi::OsString)>,
}
impl SccacheTest<'_> {
pub fn new(additional_envs: Option<&[(&'static str, std::ffi::OsString)]>) -> Result<Self> {
assert!(LOGGER.is_ok());
let tempdir = tempfile::Builder::new()
.prefix("sccache_test_rust_cargo")
.tempdir()
.context("Failed to create tempdir")?;
let cache_dir = tempdir.path().join("cache");
fs::create_dir(&cache_dir)?;
let cargo_dir = tempdir.path().join("cargo");
fs::create_dir(&cargo_dir)?;
stop_sccache()?;
trace!("sccache --start-server");
Command::new(SCCACHE_BIN.as_os_str())
.arg("--start-server")
.env("SCCACHE_DIR", &cache_dir)
.assert()
.try_success()
.context("Failed to start sccache server")?;
let mut env = vec![
("CARGO_TARGET_DIR", cargo_dir.as_os_str().to_owned()),
("RUSTC_WRAPPER", SCCACHE_BIN.as_os_str().to_owned()),
("CARGO_INCREMENTAL", OsString::from("0")),
("TEST_ENV_VAR", OsString::from("1")),
];
if let Some(vec) = additional_envs {
env.extend_from_slice(vec);
}
Ok(SccacheTest {
tempdir,
env: env.clone(),
})
}
pub fn show_stats(&self) -> assert_cmd::assert::AssertResult {
trace!("sccache --show-stats");
Command::new(SCCACHE_BIN.as_os_str())
.args(["--show-stats", "--stats-format=json"])
.assert()
.try_success()
}
pub fn show_text_stats(&self, advanced: bool) -> assert_cmd::assert::AssertResult {
let cmd = if advanced {
"--show-adv-stats"
} else {
"--show-stats"
};
trace!("sccache {cmd}");
Command::new(SCCACHE_BIN.as_os_str())
.args([cmd, "--stats-format=text"])
.assert()
.try_success()
}
}
impl Drop for SccacheTest<'_> {
fn drop(&mut self) {
stop_sccache().expect("Stopping Sccache server failed");
}
}
pub fn stop_sccache() -> Result<()> {
trace!("sccache --stop-server");
Command::new(SCCACHE_BIN.as_os_str())
.arg("--stop-server")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.context("Failed to stop sccache server")?;
Ok(())
}
pub fn cargo_clean(test_info: &SccacheTest) -> Result<()> {
Command::new(CARGO.as_os_str())
.args(["clean"])
.envs(test_info.env.iter().cloned())
.current_dir(CRATE_DIR.as_os_str())
.assert()
.try_success()?;
Ok(())
}