use std::path::PathBuf;
use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
use pounce_cli::solve_report::SolveReport;
fn pounce_exe() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_pounce"))
}
fn fixture(name: &str) -> PathBuf {
let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
p.push("tests");
p.push("fixtures");
p.push(name);
p
}
fn tmp_path(suffix: &str) -> PathBuf {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let mut p = std::env::temp_dir();
p.push(format!(
"pounce_issue257_{}_{}_{suffix}",
std::process::id(),
n
));
p
}
fn solve(fixture_name: &str, extra_opts: &[&str]) -> SolveReport {
let json_path = tmp_path(&format!("{fixture_name}.json"));
let sol_path = tmp_path(&format!("{fixture_name}.sol"));
let mut cmd = Command::new(pounce_exe());
cmd.arg(fixture(fixture_name))
.arg(&sol_path)
.arg("--json-output")
.arg(&json_path);
for opt in extra_opts {
cmd.arg(opt);
}
let _ = cmd.status().expect("spawn pounce");
let text = std::fs::read_to_string(&json_path).expect("read json report");
let _ = std::fs::remove_file(&json_path);
let _ = std::fs::remove_file(&sol_path);
serde_json::from_str(&text).expect("deserialize SolveReport")
}
const NODE_OPTIMUM: f64 = 173_345.376_830_898_52;
fn assert_solved_at_optimum(report: &SolveReport, ctx: &str) {
let code = report.solution.solve_result_num;
assert!(
(0..100).contains(&code),
"{ctx}: did not converge (solve_result_num={code}, status={:?}); \
this node has a finite optimum ~{NODE_OPTIMUM} (issue #257)",
report.solution.status,
);
let obj = report.solution.objective;
assert!(
(obj - NODE_OPTIMUM).abs() / NODE_OPTIMUM < 1e-6,
"{ctx}: objective {obj} is not the known node optimum {NODE_OPTIMUM}",
);
}
#[test]
fn jit1_node_certifies_at_driver_tolerance() {
let report = solve("jit1_node.nl", &["tol=1e-7"]);
assert_solved_at_optimum(&report, "jit1 node (tol=1e-7)");
}
#[test]
fn jit1_node_certifies_across_loosened_tolerances() {
for tol in ["1e-8", "1e-7", "1e-6", "1e-5"] {
let report = solve("jit1_node.nl", &[&format!("tol={tol}")]);
assert_solved_at_optimum(&report, &format!("jit1 node (tol={tol})"));
}
}
#[test]
fn jit1_node_unscaled_complementarity_clears_compl_inf_tol() {
let report = solve("jit1_node.nl", &["tol=1e-7"]);
let stats = &report.statistics;
let obj_scale = stats.final_scaled_objective / stats.final_objective;
assert!(
(obj_scale - 1e-5).abs() / 1e-5 < 1e-6,
"expected this node to be scaled by df≈1e-5 (the condition that \
triggers #257); got {obj_scale} — the fixture no longer exercises \
the bug",
);
let unscaled_compl = stats.final_compl / obj_scale;
assert!(
unscaled_compl <= 1e-4,
"unscaled complementarity {unscaled_compl} exceeds compl_inf_tol=1e-4, \
so no strict certificate is reachable no matter how long the solve \
runs (issue #257)",
);
}