use super::*;
struct Knob {
var: &'static str,
good: &'static str,
bad: Option<&'static str>,
mode: &'static str,
}
const KNOBS: &[Knob] = &[
Knob {
var: "VITRI_BUDGET_MS",
good: "60000",
bad: None,
mode: "mc",
},
Knob {
var: "VITRI_PORTFOLIO_SEED",
good: "7",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_PORTFOLIO_TRACE",
good: "all",
bad: None,
mode: "mc",
},
Knob {
var: "VITRI_CONVERSION_TRACE",
good: "1",
bad: None,
mode: "mc",
},
Knob {
var: "VITRI_GOATD_REFINE_BUDGET_MS",
good: "1000",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_GOATD_CANDIDATES",
good: "2",
bad: Some("0"),
mode: "mc",
},
Knob {
var: "VITRI_GOATD_FINAL_POLISHING",
good: "off",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_PMC_FLOWCUTTER_CAP_MS",
good: "1000",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_ARJUN_SBVA",
good: "auto",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_ARJUN_EFFORT",
good: "lite",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_ARJUN_KEEP_OVERRUN",
good: "on",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_PMC_ARJUN_ORACLE_MAX_VARS",
good: "50000",
bad: Some("not-a-value"),
mode: "pmc",
},
Knob {
var: "VITRI_PWMC_ARJUN_ORACLE_MAX_VARS",
good: "50000",
bad: Some("not-a-value"),
mode: "pwmc",
},
Knob {
var: "VITRI_ARJUN_EXPORT_LEARNED_CLAUSES",
good: "1",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_ARJUN_NO_BVE",
good: "1",
bad: Some("0"),
mode: "mc",
},
Knob {
var: "VITRI_ARJUN_BVE_GROW",
good: "6",
bad: Some("not-a-value"),
mode: "mc",
},
Knob {
var: "VITRI_ARJUN_NO_ORACLE",
good: "1",
bad: Some("off"),
mode: "mc",
},
Knob {
var: "VITRI_ARJUN_SEED",
good: "12345",
bad: Some("not-a-value"),
mode: "mc",
},
];
fn instance_for(mode: &str) -> &'static str {
match mode {
"pmc" | "pwmc" => PROJECTED_WEIGHTED,
_ => IRREDUCIBLE_5,
}
}
fn run_under(t: &Scratch, knob: &Knob, tag: &str, value: &str) -> Run {
let input = t.file(&format!("{}-{tag}.cnf", knob.var), instance_for(knob.mode));
let out = t.out(&format!("{}-{tag}", knob.var));
run_with_env(
&[s(&input), "-o", s(&out), "--mode", knob.mode],
&[(knob.var, value)],
)
}
#[test]
fn every_documented_knob_accepts_the_value_its_table_row_names() {
let t = Scratch::new("knobaccept");
for knob in KNOBS {
run_under(&t, knob, "good", knob.good).exit(0);
}
}
#[test]
fn every_strict_knob_refuses_a_value_it_cannot_mean_and_exits_two() {
let t = Scratch::new("knobreject");
for knob in KNOBS {
let Some(bad) = knob.bad else { continue };
let r = run_under(&t, knob, "bad", bad).exit(2);
r.assert_stderr(knob.var);
r.assert_stderr(bad);
}
}
#[test]
fn the_empty_string_counts_as_set_for_every_strict_knob() {
let t = Scratch::new("knobempty");
for knob in KNOBS {
if knob.bad.is_none() {
continue;
}
let r = run_under(&t, knob, "empty", "").exit(2);
r.assert_stderr(knob.var);
}
}
#[test]
fn zero_is_the_spelling_of_no_cap_on_both_millisecond_knobs() {
let t = Scratch::new("knobzero");
for var in [
"VITRI_GOATD_REFINE_BUDGET_MS",
"VITRI_PMC_FLOWCUTTER_CAP_MS",
] {
let input = t.file(&format!("{var}.cnf"), IRREDUCIBLE_5);
let out = t.out(var);
run_with_env(&[s(&input), "-o", s(&out)], &[(var, "0")]).exit(0);
assert!(
out.join(VTREE_NAME).exists(),
"{var}=0 must leave construction its time, not cap it to nothing",
);
}
}
#[test]
fn a_presence_only_switch_refuses_an_off_looking_value_and_obeys_any_other() {
let t = Scratch::new("presenceonly");
let input = t.file("in.cnf", IRREDUCIBLE_5);
for var in ["VITRI_ARJUN_NO_BVE", "VITRI_ARJUN_NO_ORACLE"] {
for off in ["0", "off", "false", "FALSE"] {
let r = run_with_env(
&[s(&input), "-o", s(&t.out(&format!("{var}-{off}")))],
&[(var, off)],
)
.exit(2);
r.assert_stderr(var);
}
for on in ["1", "please"] {
run_with_env(
&[s(&input), "-o", s(&t.out(&format!("{var}-{on}")))],
&[(var, on)],
)
.exit(0);
}
}
}
#[test]
fn a_value_out_of_range_for_its_type_is_refused_not_wrapped() {
let t = Scratch::new("knobrange");
let input = t.file("in.cnf", IRREDUCIBLE_5);
for (var, value) in [
("VITRI_ARJUN_BVE_GROW", "2147483648"),
("VITRI_ARJUN_BVE_GROW", "-1"),
("VITRI_ARJUN_SEED", "-1"),
("VITRI_PORTFOLIO_SEED", "-1"),
] {
let r = run_with_env(
&[s(&input), "-o", s(&t.out(&format!("{var}{value}")))],
&[(var, value)],
)
.exit(2);
r.assert_stderr(var);
r.assert_stderr(value);
}
}
#[cfg(unix)]
#[test]
fn a_non_utf8_knob_value_is_reported_rather_than_ignored() {
use std::os::unix::ffi::OsStrExt;
let t = Scratch::new("knobbytes");
let input = t.file("in.cnf", IRREDUCIBLE_5);
let out = t.out("bundle");
let value = OsStr::from_bytes(&[b'a', 0xff, b'z']);
let r = run_with_raw_env(&[s(&input), "-o", s(&out)], &[("VITRI_ARJUN_SBVA", value)]).exit(2);
r.assert_stderr("VITRI_ARJUN_SBVA");
r.assert_stderr("UTF-8");
assert!(
!out.exists(),
"nothing may be written when the environment stopped the run",
);
}
#[test]
fn the_binary_reports_how_many_learnt_clauses_it_harvested() {
let t = Scratch::new("learntreport");
let input = t.file("in.cnf", IRREDUCIBLE_5);
let out = t.out("bundle");
let r = run_with_env(
&[s(&input), "-o", s(&out), "--mode", "mc"],
&[("VITRI_ARJUN_EXPORT_LEARNED_CLAUSES", "1")],
)
.exit(0);
r.assert_stderr("learnt clauses");
assert!(
!entries(&out).iter().any(|n| n.contains("learnt")),
"the clauses are a hint for the process holding the bundle, not a file",
);
}
#[test]
fn a_learnt_clause_export_asked_of_a_mode_that_cannot_harvest_is_refused_at_the_command_line() {
let t = Scratch::new("learntrefused");
let input = t.file("in.cnf", IRREDUCIBLE_5);
for mode in ["wmc", "compile"] {
let out = t.out(mode);
let r = run_with_env(
&[s(&input), "-o", s(&out), "--mode", mode],
&[("VITRI_ARJUN_EXPORT_LEARNED_CLAUSES", "1")],
)
.exit(2);
r.assert_stderr("VITRI_ARJUN_EXPORT_LEARNED_CLAUSES");
r.assert_stderr("preprocess under mc");
assert!(
!out.exists(),
"nothing may be written when the request was refused",
);
}
let out = t.out("stage-off");
let r = run_with_env(
&[s(&input), "-o", s(&out), "--mode", "mc", "--no-arjun"],
&[("VITRI_ARJUN_EXPORT_LEARNED_CLAUSES", "1")],
)
.exit(2);
r.assert_stderr("--no-arjun");
}
#[test]
fn a_malformed_knob_stops_the_run_before_any_work() {
let t = Scratch::new("badenv");
let input = t.file("in.cnf", IRREDUCIBLE_5);
for var in ["VITRI_ARJUN_SBVA", "VITRI_PORTFOLIO_SEED"] {
let out = t.out(&format!("bundle-{var}"));
let r = run_with_env(&[s(&input), "-o", s(&out)], &[(var, "not-a-value")]).exit(2);
r.assert_stderr("environment variable");
r.assert_stderr(var);
r.assert_stderr("not-a-value");
r.assert_stderr("docs/env.md");
assert!(
!r.stderr.contains("--help"),
"the usage text does not document variables:\n{}",
r.stderr,
);
assert!(
!out.exists(),
"nothing may be written when the environment stopped the run",
);
}
}
#[test]
fn help_answers_under_a_malformed_knob() {
let r = run_with_env(&["--help"], &[("VITRI_ARJUN_SBVA", "bogus")]).exit(0);
r.assert_stdout("USAGE:");
assert!(r.stderr.is_empty(), "help belongs on stdout: {}", r.stderr);
}
#[test]
fn the_budget_hint_is_the_one_tolerant_knob() {
let t = Scratch::new("walltolerant");
let input = t.file("in.cnf", IRREDUCIBLE_5);
let out = t.out("bundle");
run_with_env(
&[s(&input), "-o", s(&out)],
&[("VITRI_BUDGET_MS", "not-a-number")],
)
.exit(0);
assert!(out.join(PREPROCESS_RECORD_NAME).exists());
}