use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::Command;
use crate::kernel::{encode_kernel_list, resolve_kernel_set};
pub(crate) const TEST_SUB_ARGV: &[&str] = &["nextest", "run"];
pub(crate) const COVERAGE_SUB_ARGV: &[&str] = &["llvm-cov", "nextest"];
pub(crate) const LLVM_COV_SUB_ARGV: &[&str] = &["llvm-cov"];
pub(crate) fn profraw_inject_for(
sub_argv: &[&str],
existing_env: Option<std::ffi::OsString>,
) -> Option<PathBuf> {
if sub_argv != TEST_SUB_ARGV || existing_env.is_some() {
return None;
}
let dir = ktstr::test_support::profraw_target_dir();
Some(dir.join("default-%p-%m.profraw"))
}
fn run_cargo_sub(
sub_argv: &[&str],
label: &str,
kernel: Vec<String>,
no_perf_mode: bool,
release: bool,
args: Vec<String>,
) -> Result<(), String> {
let mut cmd = Command::new("cargo");
cmd.args(sub_argv);
if release {
cmd.args(["--cargo-profile", "release"]);
}
cmd.args(&args);
if no_perf_mode {
cmd.env("KTSTR_NO_PERF_MODE", "1");
}
if let Some(pat) = profraw_inject_for(sub_argv, std::env::var_os("LLVM_PROFILE_FILE")) {
cmd.env("LLVM_PROFILE_FILE", pat);
}
if !kernel.is_empty() {
let resolved = resolve_kernel_set(&kernel)?;
if resolved.is_empty() {
return Err(
"--kernel: every supplied value parsed to empty / whitespace; \
omit the flag for auto-discovery, or supply a kernel \
identifier"
.to_string(),
);
}
let first_dir = &resolved[0].1;
eprintln!("cargo ktstr: using kernel {}", first_dir.display());
cmd.env(ktstr::KTSTR_KERNEL_ENV, first_dir);
if resolved.len() > 1 {
let encoded = encode_kernel_list(&resolved)?;
eprintln!(
"cargo ktstr: fanning gauntlet across {n} kernels",
n = resolved.len(),
);
cmd.env(ktstr::KTSTR_KERNEL_LIST_ENV, encoded);
}
}
eprintln!("cargo ktstr: running {label}");
let err = cmd.exec();
Err(format!("exec cargo {}: {err}", sub_argv.join(" ")))
}
pub(crate) fn run_test(
kernel: Vec<String>,
no_perf_mode: bool,
release: bool,
args: Vec<String>,
) -> Result<(), String> {
run_cargo_sub(TEST_SUB_ARGV, "tests", kernel, no_perf_mode, release, args)
}
pub(crate) fn run_coverage(
kernel: Vec<String>,
no_perf_mode: bool,
release: bool,
args: Vec<String>,
) -> Result<(), String> {
run_cargo_sub(
COVERAGE_SUB_ARGV,
"coverage",
kernel,
no_perf_mode,
release,
args,
)
}
pub(crate) fn run_llvm_cov(
kernel: Vec<String>,
no_perf_mode: bool,
args: Vec<String>,
) -> Result<(), String> {
run_cargo_sub(
LLVM_COV_SUB_ARGV,
"llvm-cov",
kernel,
no_perf_mode,
false,
args,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cargo_sub_argv_constants_are_pinned() {
assert_eq!(TEST_SUB_ARGV, &["nextest", "run"]);
assert_eq!(COVERAGE_SUB_ARGV, &["llvm-cov", "nextest"]);
assert_eq!(LLVM_COV_SUB_ARGV, &["llvm-cov"]);
}
#[test]
fn profraw_inject_for_test_path_returns_pattern() {
let pat = profraw_inject_for(TEST_SUB_ARGV, None)
.expect("test path without LLVM_PROFILE_FILE must inject");
assert!(
pat.ends_with("default-%p-%m.profraw"),
"injected pattern must end with default-%%p-%%m.profraw, got {}",
pat.display(),
);
assert_ne!(
pat.as_os_str(),
"default-%p-%m.profraw",
"pattern must be absolute (carry a target dir prefix), \
not bare so the LLVM runtime never falls back to cwd",
);
}
#[test]
fn profraw_inject_for_coverage_path_skips() {
assert!(
profraw_inject_for(COVERAGE_SUB_ARGV, None).is_none(),
"coverage path must not inject — cargo-llvm-cov owns LLVM_PROFILE_FILE",
);
}
#[test]
fn profraw_inject_for_llvm_cov_path_skips() {
assert!(
profraw_inject_for(LLVM_COV_SUB_ARGV, None).is_none(),
"llvm-cov passthrough path must not inject — user owns env decisions",
);
}
#[test]
fn profraw_inject_for_respects_operator_override() {
let existing = std::ffi::OsString::from("/tmp/operator-pinned-%p.profraw");
assert!(
profraw_inject_for(TEST_SUB_ARGV, Some(existing)).is_none(),
"an operator-set LLVM_PROFILE_FILE must not be overridden",
);
}
}