Skip to content

Commit 914f1bc

Browse files
committed
feat: enable proof logging in appropriate test cases (#154)
Only logs the proof; does not check at the moment
1 parent ea53fed commit 914f1bc

File tree

6 files changed

+100
-50
lines changed

6 files changed

+100
-50
lines changed

pumpkin-macros/src/lib.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ pub fn cumulative(item: TokenStream) -> TokenStream {
3535
stringcase::snake_case(&argument[2..].split("-").skip(1).join("_"))
3636
})
3737
.join("_");
38-
let options = quote! {
39-
#(#options),*
40-
};
38+
4139
let test_name = format_ident!(
4240
"{}",
4341
stringcase::snake_case(
@@ -53,12 +51,18 @@ pub fn cumulative(item: TokenStream) -> TokenStream {
5351
)
5452
);
5553
let stream: TokenStream = quote! {
56-
mzn_test!(
57-
#test_name,
58-
"cumulative",
59-
vec!["--cumulative-propagation-method", &stringcase::kebab_case(#propagation_method),"--cumulative-explanation-type", #explanation_type, #options]
60-
);
61-
}
54+
mzn_test!(
55+
#test_name,
56+
"cumulative",
57+
vec![
58+
"--cumulative-propagation-method".to_string(),
59+
stringcase::kebab_case(#propagation_method),
60+
"--cumulative-explanation-type".to_string(),
61+
#explanation_type.to_string(),
62+
#(#options.to_string()),*
63+
]
64+
);
65+
}
6266
.into();
6367
output.extend(stream);
6468
}
@@ -102,9 +106,6 @@ pub fn cumulative_synchronised(item: TokenStream) -> TokenStream {
102106
.iter()
103107
.map(|argument| stringcase::snake_case(&argument[2..].split("-").skip(1).join("_")))
104108
.join("_");
105-
let options = quote! {
106-
#(#options),*
107-
};
108109
let test_name = format_ident!(
109110
"{}",
110111
stringcase::snake_case(
@@ -128,8 +129,20 @@ pub fn cumulative_synchronised(item: TokenStream) -> TokenStream {
128129
check_statistic_equality(
129130
"cumulative",
130131
"mzn_constraints",
131-
vec!["--cumulative-propagation-method", &stringcase::kebab_case(stringify!(#first_name)),"--cumulative-explanation-type", #explanation_type, #options],
132-
vec!["--cumulative-propagation-method", &stringcase::kebab_case(stringify!(#second_name)),"--cumulative-explanation-type", #explanation_type, #options],
132+
vec![
133+
"--cumulative-propagation-method".to_string(),
134+
stringcase::kebab_case(stringify!(#first_name)),
135+
"--cumulative-explanation-type".to_string(),
136+
#explanation_type.to_string(),
137+
#(#options.to_string()),*
138+
],
139+
vec![
140+
"--cumulative-propagation-method".to_string(),
141+
stringcase::kebab_case(stringify!(#second_name)),
142+
"--cumulative-explanation-type".to_string(),
143+
#explanation_type.to_string(),
144+
#(#options.to_string()),*
145+
],
133146
&format!("equality_{}_{}_{}", #first_name, #explanation_type, #option_string),
134147
&format!("equality_{}_{}_{}", #second_name, #explanation_type, #option_string),
135148
);

pumpkin-solver/tests/helpers/mod.rs

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ pub(crate) fn run_solver(instance_path: impl AsRef<Path>, with_proof: bool) -> F
4242
run_solver_with_options(instance_path, with_proof, std::iter::empty(), None)
4343
}
4444

45-
pub(crate) fn run_solver_with_options<'a>(
45+
pub(crate) fn run_solver_with_options(
4646
instance_path: impl AsRef<Path>,
4747
with_proof: bool,
48-
args: impl IntoIterator<Item = &'a str>,
48+
args: impl IntoIterator<Item = String>,
4949
prefix: Option<&str>,
5050
) -> Files {
5151
let args = args.into_iter().collect::<Vec<_>>();
@@ -57,7 +57,7 @@ pub(crate) fn run_solver_with_options<'a>(
5757
let solver = PathBuf::from(env!("CARGO_BIN_EXE_pumpkin-solver"));
5858

5959
let add_extension = |extension: &str| -> PathBuf {
60-
if let Some(prefix) = prefix {
60+
if let Some(prefix) = prefix.and_then(|s| if s.is_empty() { None } else { Some(s) }) {
6161
instance_path.with_extension(format!("{prefix}.{extension}"))
6262
} else {
6363
instance_path.with_extension(extension)
@@ -184,15 +184,19 @@ pub(crate) fn verify_proof(files: Files, checker_output: &Output) -> std::io::Re
184184
files.cleanup()
185185
}
186186

187-
pub(crate) fn run_mzn_test<const ORDERED: bool>(instance_name: &str, folder_name: &str) {
188-
run_mzn_test_with_options::<ORDERED>(instance_name, folder_name, vec![], "")
187+
pub(crate) fn run_mzn_test<const ORDERED: bool>(
188+
instance_name: &str,
189+
folder_name: &str,
190+
test_type: TestType,
191+
) -> String {
192+
run_mzn_test_with_options::<ORDERED>(instance_name, folder_name, test_type, vec![], "")
189193
}
190194

191195
pub(crate) fn check_statistic_equality(
192196
instance_name: &str,
193197
folder_name: &str,
194-
mut options_first: Vec<&str>,
195-
mut options_second: Vec<&str>,
198+
mut options_first: Vec<String>,
199+
mut options_second: Vec<String>,
196200
prefix_first: &str,
197201
prefix_second: &str,
198202
) {
@@ -201,8 +205,8 @@ pub(crate) fn check_statistic_equality(
201205
env!("CARGO_MANIFEST_DIR")
202206
);
203207

204-
options_first.push("-sa");
205-
options_second.push("-sa");
208+
options_first.push("-sa".to_owned());
209+
options_second.push("-sa".to_owned());
206210

207211
let files_first = run_solver_with_options(
208212
instance_path.clone(),
@@ -246,12 +250,20 @@ pub(crate) fn check_statistic_equality(
246250
)
247251
}
248252

253+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
254+
pub(crate) enum TestType {
255+
Unsatisfiable,
256+
SolutionEnumeration,
257+
Optimality,
258+
}
259+
249260
pub(crate) fn run_mzn_test_with_options<const ORDERED: bool>(
250261
instance_name: &str,
251262
folder_name: &str,
252-
mut options: Vec<&str>,
263+
test_type: TestType,
264+
mut options: Vec<String>,
253265
prefix: &str,
254-
) {
266+
) -> String {
255267
let instance_path = format!(
256268
"{}/tests/{folder_name}/{instance_name}.fzn",
257269
env!("CARGO_MANIFEST_DIR")
@@ -262,22 +274,39 @@ pub(crate) fn run_mzn_test_with_options<const ORDERED: bool>(
262274
env!("CARGO_MANIFEST_DIR")
263275
);
264276

265-
options.push("-a");
277+
if matches!(
278+
test_type,
279+
TestType::SolutionEnumeration | TestType::Optimality
280+
) {
281+
// Both for optimisation and enumeration do we want to log all encountered solutions.
282+
options.push("-a".to_owned());
283+
}
266284

267-
let files = run_solver_with_options(instance_path, false, options, Some(prefix));
285+
let files = run_solver_with_options(
286+
instance_path,
287+
matches!(test_type, TestType::Unsatisfiable | TestType::Optimality),
288+
options,
289+
Some(prefix),
290+
);
268291

269292
let output = std::fs::read_to_string(files.log_file).expect("Failed to read solver output");
270293

271-
let expected_file =
272-
std::fs::read_to_string(snapshot_path).expect("Failed to read expected solution file.");
294+
if test_type == TestType::Unsatisfiable {
295+
assert!(output.contains("=====UNSATISFIABLE====="));
296+
} else {
297+
let expected_file =
298+
std::fs::read_to_string(snapshot_path).expect("Failed to read expected solution file.");
273299

274-
let actual_solutions = output
275-
.parse::<Solutions<ORDERED>>()
276-
.expect("Valid solution");
300+
let actual_solutions = output
301+
.parse::<Solutions<ORDERED>>()
302+
.expect("Valid solution");
277303

278-
let expected_solutions = expected_file
279-
.parse::<Solutions<ORDERED>>()
280-
.expect("Valid solution");
304+
let expected_solutions = expected_file
305+
.parse::<Solutions<ORDERED>>()
306+
.expect("Valid solution");
307+
308+
assert_eq!(actual_solutions, expected_solutions, "Did not find the elements {:?} in the expected solution and the expected solution contained {:?} while the actual solution did not.", actual_solutions.assignments.iter().filter(|solution| !expected_solutions.assignments.contains(solution)).collect::<Vec<_>>(), expected_solutions.assignments.iter().filter(|solution| !actual_solutions.assignments.contains(solution)).collect::<Vec<_>>());
309+
}
281310

282-
assert_eq!(actual_solutions, expected_solutions, "Did not find the elements {:?} in the expected solution and the expected solution contained {:?} while the actual solution did not.", actual_solutions.assignments.iter().filter(|solution| !expected_solutions.assignments.contains(solution)).collect::<Vec<_>>(), expected_solutions.assignments.iter().filter(|solution| !actual_solutions.assignments.contains(solution)).collect::<Vec<_>>());
311+
output
283312
}

pumpkin-solver/tests/mzn_constraint_test.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod helpers;
44
use helpers::check_statistic_equality;
55
use helpers::run_mzn_test_with_options;
6+
use helpers::TestType;
67
use pumpkin_macros::cumulative;
78
use pumpkin_macros::cumulative_synchronised;
89

@@ -14,12 +15,14 @@ macro_rules! mzn_test {
1415
($name:ident, $file:expr, $options:expr) => {
1516
#[test]
1617
fn $name() {
17-
run_mzn_test_with_options::<false>(
18+
let output = run_mzn_test_with_options::<false>(
1819
$file,
1920
"mzn_constraints",
21+
TestType::SolutionEnumeration,
2022
$options,
2123
stringify!($name),
2224
);
25+
assert!(output.ends_with("==========\n"));
2326
}
2427
};
2528
}

pumpkin-solver/tests/mzn_infeasible_test.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
mod helpers;
44

5-
use helpers::run_solver_with_options;
5+
use helpers::run_mzn_test;
6+
use helpers::TestType;
67

78
macro_rules! mzn_infeasible_test {
89
($name:ident) => {
@@ -15,13 +16,5 @@ macro_rules! mzn_infeasible_test {
1516
mzn_infeasible_test!(prop_stress);
1617

1718
pub fn run_mzn_infeasible_test(instance_name: &str, folder_name: &str) {
18-
let instance_path = format!(
19-
"{}/tests/{folder_name}/{instance_name}.fzn",
20-
env!("CARGO_MANIFEST_DIR")
21-
);
22-
23-
let files = run_solver_with_options(instance_path, false, ["-a"], None);
24-
25-
let output = std::fs::read_to_string(files.log_file).expect("Failed to read solver output");
26-
assert!(output.ends_with("=====UNSATISFIABLE=====\n"));
19+
let _ = run_mzn_test::<false>(instance_name, folder_name, TestType::Unsatisfiable);
2720
}

pumpkin-solver/tests/mzn_optimization_test.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
mod helpers;
44

55
use helpers::run_mzn_test;
6+
use helpers::TestType;
67

78
macro_rules! mzn_optimization_test {
89
($name:ident) => {
910
#[test]
1011
fn $name() {
11-
run_mzn_test::<false>(stringify!($name), "mzn_optimization");
12+
let output =
13+
run_mzn_test::<false>(stringify!($name), "mzn_optimization", TestType::Optimality);
14+
assert!(output.ends_with("==========\n"));
1215
}
1316
};
1417
}

pumpkin-solver/tests/mzn_search_test.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
mod helpers;
44

55
use helpers::run_mzn_test;
6+
use helpers::TestType;
67

78
macro_rules! mzn_search_ordered {
89
($name:ident) => {
910
#[test]
1011
fn $name() {
11-
run_mzn_test::<true>(stringify!($name), "mzn_search");
12+
let _ = run_mzn_test::<true>(
13+
stringify!($name),
14+
"mzn_search",
15+
TestType::SolutionEnumeration,
16+
);
1217
}
1318
};
1419
}
@@ -17,7 +22,11 @@ macro_rules! mzn_search_unordered {
1722
($name:ident) => {
1823
#[test]
1924
fn $name() {
20-
run_mzn_test::<false>(stringify!($name), "mzn_search");
25+
let _ = run_mzn_test::<false>(
26+
stringify!($name),
27+
"mzn_search",
28+
TestType::SolutionEnumeration,
29+
);
2130
}
2231
};
2332
}

0 commit comments

Comments
 (0)