Skip to main content

conjure_oxide/
test_solve.rs

1use std::path::PathBuf;
2use std::process::exit;
3use std::sync::Arc;
4
5use crate::cli::GlobalArgs;
6use crate::solve::{self, init_solver};
7use clap::ValueHint;
8use conjure_cp::instantiate::instantiate_model;
9use conjure_cp_cli::utils::conjure::{
10    get_solutions, get_solutions_from_conjure, solutions_to_json,
11};
12use conjure_cp_cli::utils::testing::normalize_solutions_for_comparison;
13
14#[derive(Clone, Debug, clap::Args)]
15pub struct Args {
16    /// The input Essence problem file
17    #[arg(value_name = "INPUT_ESSENCE",value_hint=ValueHint::FilePath)]
18    pub input_file: PathBuf,
19
20    /// The input Essence parameter file
21    #[arg(value_name = "PARAM_ESSENCE", value_hint=ValueHint::FilePath)]
22    pub param_file: Option<PathBuf>,
23}
24
25pub fn run_test_solve_command(global_args: GlobalArgs, local_args: Args) -> anyhow::Result<()> {
26    // stealing most of the steps of the solve command, except the solver stuff.
27    let input_file = local_args.input_file;
28    let param_file = local_args.param_file;
29
30    // each step is in its own method so that similar commands
31    // (e.g. testsolve) can reuse some of these steps.
32
33    let context = solve::init_context(&global_args, input_file.clone(), param_file.clone())?;
34
35    // get input and param file name from context
36    let ctx_lock = context.read().unwrap();
37    let essence_file_name = ctx_lock
38        .essence_file_name
39        .as_ref()
40        .expect("context should contain the problem input file");
41    let param_file_name = ctx_lock.param_file_name.as_ref();
42
43    // parse models
44    let problem_model = solve::parse(&global_args, Arc::clone(&context), essence_file_name)?;
45
46    let unified_model = match param_file_name {
47        Some(param_file_name) => {
48            let param_model = solve::parse(&global_args, Arc::clone(&context), param_file_name)?;
49            instantiate_model(problem_model, param_model)?
50        }
51        None => problem_model,
52    };
53
54    drop(ctx_lock);
55
56    let rewritten_model = solve::rewrite(unified_model, &global_args, Arc::clone(&context))?;
57
58    let solver = init_solver(&global_args);
59
60    // now we are stealing from the integration tester
61
62    let our_solutions = get_solutions(
63        solver,
64        rewritten_model,
65        0,
66        &global_args.save_solver_input_file,
67    )?;
68
69    let conjure_solutions = get_solutions_from_conjure(
70        input_file.to_str().unwrap(),
71        param_file.as_deref().map(|f| f.to_str().unwrap()),
72        Arc::clone(&context),
73    )?;
74
75    let our_solutions = normalize_solutions_for_comparison(&our_solutions);
76    let conjure_solutions = normalize_solutions_for_comparison(&conjure_solutions);
77
78    let mut our_solutions_json = solutions_to_json(&our_solutions);
79    let mut conjure_solutions_json = solutions_to_json(&conjure_solutions);
80
81    our_solutions_json.sort_all_objects();
82    conjure_solutions_json.sort_all_objects();
83
84    if our_solutions_json == conjure_solutions_json {
85        eprintln!("Success: solutions match!");
86        exit(0);
87    } else {
88        eprintln!("=== our solutions:");
89        eprintln!("{our_solutions_json}");
90        eprintln!("=== conjure's solutions:");
91        eprintln!("{conjure_solutions_json}");
92        eprintln!("Failure: solutions do not match!");
93        exit(1);
94    }
95}