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_issue248_{}_{}_{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 status = cmd.status().expect("spawn pounce");
let _ = status;
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 JIT1_OPTIMUM: f64 = 173_345.0;
fn assert_not_unbounded_at_optimum(report: &SolveReport, ctx: &str) {
let code = report.solution.solve_result_num;
assert!(
!(300..400).contains(&code),
"{ctx}: reported UNBOUNDED (solve_result_num={code}, status={:?}); \
jit1 is bounded below with a finite optimum (issue #248)",
report.solution.status,
);
let obj = report.solution.objective;
assert!(
(obj - JIT1_OPTIMUM).abs() / JIT1_OPTIMUM < 1e-3,
"{ctx}: objective {obj} is not the known optimum ~{JIT1_OPTIMUM}",
);
}
#[test]
fn jit1_default_is_not_unbounded() {
let report = solve("jit1.nl", &[]);
assert_not_unbounded_at_optimum(&report, "jit1 (default)");
}
#[test]
fn jit1_free_vars_low_diverging_tol_is_not_unbounded() {
let report = solve("jit1.nl", &["diverging_iterates_tol=2"]);
assert_not_unbounded_at_optimum(&report, "jit1 (free vars, diverging_iterates_tol=2)");
}
#[test]
fn jit1_boxed_is_not_unbounded_even_with_low_diverging_tol() {
let report = solve("jit1_boxed.nl", &["diverging_iterates_tol=2"]);
assert_not_unbounded_at_optimum(&report, "jit1 boxed (diverging_iterates_tol=2)");
}