use std::path::PathBuf;
use std::process::Command;
fn pounce_exe() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_pounce"))
}
fn run(fixture_name: &str, tag: &str, opts: &[&str]) -> (Option<i32>, String) {
let dir = std::env::temp_dir().join(format!("pounce_unimplopt_{tag}"));
std::fs::create_dir_all(&dir).expect("scratch dir");
let nl = dir.join(fixture_name);
let mut fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
fixture.push("tests/fixtures");
fixture.push(fixture_name);
std::fs::copy(&fixture, &nl).expect("copy fixture");
let out = Command::new(pounce_exe())
.arg(&nl)
.args(opts)
.arg("print_level=0")
.output()
.expect("run pounce");
let _ = std::fs::remove_dir_all(&dir);
(
out.status.code(),
String::from_utf8_lossy(&out.stderr).into_owned(),
)
}
#[test]
fn requesting_an_unimplemented_feature_fails_with_an_explanation() {
for (i, (opt, needle)) in [
("penalty_init_max=42", "CG-penalty"),
(
"gradient_approximation=finite-difference-values",
"finite differences",
),
("dependency_detector=mumps", "linear-dependency detection"),
("check_derivatives_for_naninf=yes", "NaN/Inf"),
("recalc_y=yes", "multiplier recalculation"),
("magic_steps=yes", "magic steps"),
("suppress_all_output=yes", "output controls"),
("hsllib=libcoinhsl.so", "HSL loader"),
]
.into_iter()
.enumerate()
{
let (code, err) = run("user_scaling_suffix.nl", &format!("g{i}"), &[opt]);
assert_eq!(code, Some(2), "`{opt}` should fail; stderr:\n{err}");
assert!(
err.contains(needle),
"`{opt}` should mention `{needle}`; stderr:\n{err}",
);
assert!(err.contains("483"), "stderr:\n{err}");
}
}
#[test]
fn the_refusal_names_the_offending_option() {
let (_, err) = run("user_scaling_suffix.nl", "named", &["vartheta=0.9"]);
assert!(err.contains("`vartheta`"), "stderr:\n{err}");
}
#[test]
fn explicitly_setting_a_default_still_solves() {
for (i, opt) in ["dependency_detector=none", "magic_steps=no", "recalc_y=no"]
.into_iter()
.enumerate()
{
let (code, err) = run("user_scaling_suffix.nl", &format!("d{i}"), &[opt]);
assert_eq!(code, Some(0), "`{opt}` asks for nothing; stderr:\n{err}");
assert!(!err.contains("does not implement"), "stderr:\n{err}");
}
}
#[test]
fn knobs_on_implemented_features_still_solve() {
for (i, opt) in [
"max_resto_iter=17",
"accept_after_max_steps=3",
"limited_memory_max_skipping=4",
"corrector_type=affine",
]
.into_iter()
.enumerate()
{
let (code, err) = run("user_scaling_suffix.nl", &format!("b{i}"), &[opt]);
assert_eq!(
code,
Some(0),
"`{opt}` configures an implemented feature; stderr:\n{err}",
);
}
}
#[test]
fn a_caching_hint_warns_but_solves() {
let (code, err) = run("user_scaling_suffix.nl", "hint", &["hessian_constant=yes"]);
assert_eq!(code, Some(0), "stderr:\n{err}");
assert!(err.contains("warning"), "stderr:\n{err}");
assert!(err.contains("hessian_constant"), "stderr:\n{err}");
}
#[test]
fn the_refusal_covers_the_convex_route() {
let (code, err) = run("boxed_qp_min.nl", "convex", &["magic_steps=yes"]);
assert_eq!(code, Some(2), "stderr:\n{err}");
assert!(err.contains("magic steps"), "stderr:\n{err}");
}
#[test]
fn a_default_run_is_silent() {
let (code, err) = run("user_scaling_suffix.nl", "plain", &[]);
assert_eq!(code, Some(0), "stderr:\n{err}");
assert!(!err.contains("does not implement"), "stderr:\n{err}");
assert!(!err.contains("warning:"), "stderr:\n{err}");
}