#[allow(dead_code)]
pub fn skip(language: &str, reason: &str) {
let variable = format!("SUPERCOV_REQUIRE_{}", language.to_ascii_uppercase());
if std::env::var(&variable).as_deref() == Ok("1") {
panic!(
"{variable} is set, so this test must run rather than skip, and it cannot: {reason}"
);
}
eprintln!("[{language}] skipped: {reason}");
}
#[allow(dead_code)]
pub fn version_pair(version: &str) -> Option<(u32, u32)> {
let digits = version
.trim()
.trim_start_matches(|c: char| !c.is_ascii_digit());
let mut parts = digits.split('.');
let major = parts.next()?.parse().ok()?;
let minor = parts
.next()
.map(|minor| minor.trim_end_matches(|c: char| !c.is_ascii_digit()))
.unwrap_or("0")
.parse()
.ok()?;
Some((major, minor))
}
#[allow(dead_code)]
pub fn at_least(version: &str, minimum: &str) -> bool {
match (version_pair(version), version_pair(minimum)) {
(Some(found), Some(needed)) => found >= needed,
_ => false,
}
}
#[allow(dead_code)]
pub fn skip_below_version(language: &str, needed: &str, found: &str) {
let variable = format!("SUPERCOV_REQUIRE_{}_LANG", language.to_ascii_uppercase());
let promised = std::env::var(&variable).ok();
if skipping_is_a_failure(promised.as_deref(), needed) {
let promised = promised.unwrap_or_default();
panic!(
"{variable}={promised} promises {language} {needed} or newer, so this test must run rather than skip -- and the toolchain here reports {found}"
);
}
eprintln!("[{language}] skipped: needs {needed} or newer, found {found}");
}
#[allow(dead_code)]
pub fn skipping_is_a_failure(promised: Option<&str>, needed: &str) -> bool {
promised.is_some_and(|promised| at_least(promised, needed))
}
#[allow(dead_code)]
pub fn tool(name: &str) -> Option<std::path::PathBuf> {
let extensions: &[&str] = if cfg!(windows) {
&["", ".cmd", ".bat", ".exe"]
} else {
&[""]
};
let prefixes = [
"",
"/opt/homebrew/bin/",
"/opt/homebrew/opt/openjdk/bin/",
"/usr/local/bin/",
"/usr/local/go/bin/",
"/usr/bin/",
];
for prefix in prefixes {
for extension in extensions {
let path = std::path::PathBuf::from(format!("{prefix}{name}{extension}"));
for flag in ["--version", "-version"] {
if std::process::Command::new(&path)
.arg(flag)
.output()
.is_ok_and(|out| out.status.success())
{
return Some(path);
}
}
}
}
None
}
#[allow(dead_code)]
pub fn resolvable(language: &str, probe: impl FnOnce() -> bool) -> bool {
let variable = format!("SUPERCOV_REQUIRE_{}", language.to_ascii_uppercase());
if std::env::var(&variable).as_deref() == Ok("1") {
return true;
}
probe()
}
#[allow(dead_code)]
pub fn classpath(entries: &[&str]) -> String {
entries.join(if cfg!(windows) { ";" } else { ":" })
}
#[allow(dead_code)]
pub fn java_release(javac: &std::path::Path) -> Option<u32> {
let output = std::process::Command::new(javac)
.arg("-version")
.output()
.ok()?;
let text = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
text.split_whitespace()
.find_map(|word| word.split('.').next()?.parse::<u32>().ok())
}
#[allow(dead_code)]
pub fn building() -> std::sync::MutexGuard<'static, ()> {
static BUILD: std::sync::Mutex<()> = std::sync::Mutex::new(());
BUILD
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}