1use core::{error, fmt};
10use std::{io::Write, process::Command};
11
12use easy_smt::{Context, ContextBuilder};
13use expression_encoding::SMTSolverError;
14use log::{debug, error, trace, warn};
15
16#[cfg(feature = "config_deserialize")]
17use serde::Deserialize;
18
19pub mod expression_encoding;
20
21pub const Z3_PRG: &str = "z3";
23pub const Z3_ARGS: [&str; 3] = ["-smt2", "-in", "-v:0"];
25
26pub const CVC5_PRG: &str = "cvc5";
28pub const CVC5_ARGS: [&str; 3] = ["--quiet", "--lang=smt2", "--incremental"];
30
31pub type SMTSolver = Context;
36
37pub type SMTExpr = easy_smt::SExpr;
41
42type CompatibilityCheck = fn((i32, i32, i32));
51
52#[derive(Debug, Clone)]
54#[cfg_attr(feature = "config_deserialize", derive(Deserialize))]
55pub struct SMTSolverBuilderCfg {
56 command: String,
58 #[cfg_attr(feature = "config_deserialize", serde(default))]
60 args: Vec<String>,
61 #[cfg_attr(feature = "config_deserialize", serde(default))]
63 opts: Vec<SMTSolverOption>,
64 #[cfg_attr(feature = "config_deserialize", serde(default = "default_set_lia"))]
66 set_lia: bool,
67 #[cfg_attr(feature = "config_deserialize", serde(skip))]
69 check_version: Option<CompatibilityCheck>,
70}
71
72impl PartialEq for SMTSolverBuilderCfg {
73 fn eq(&self, other: &Self) -> bool {
75 self.command == other.command
76 && self.args == other.args
77 && self.opts == other.opts
78 && self.set_lia == other.set_lia
79 && self.check_version.is_some() == other.check_version.is_some()
80 }
81}
82
83#[cfg(feature = "config_deserialize")]
85fn default_set_lia() -> bool {
86 false
87}
88
89impl SMTSolverBuilderCfg {
90 pub fn new(
102 command: String,
103 args: Vec<String>,
104 opts: Vec<SMTSolverOption>,
105 set_lia: bool,
106 ) -> Self {
107 Self {
108 command,
109 args,
110 opts,
111 set_lia,
112 check_version: None,
113 }
114 }
115
116 pub fn new_z3() -> Self {
118 let opts = Vec::new();
119
120 Self {
121 command: Z3_PRG.to_string(),
122 args: Z3_ARGS.iter().map(|s| s.to_string()).collect(),
123 opts,
124 set_lia: true,
125 check_version: None,
126 }
127 }
128
129 pub fn new_cvc5() -> Self {
131 let check_version = |version: (i32, i32, i32)| {
132 if version.0 <= 1 && version.1 < 1 {
133 warn!(
134 "Detected cvc5 < v1.1.0 (cvc5 is version {}.{}.{}). This version is not officially supported !",
135 version.0, version.1, version.2
136 )
137 }
138 };
139
140 Self {
141 command: CVC5_PRG.to_string(),
142 args: CVC5_ARGS.iter().map(|s| s.to_string()).collect(),
143 opts: vec![],
144 set_lia: true,
145 check_version: Some(check_version),
146 }
147 }
148}
149
150#[derive(Debug, PartialEq, Clone)]
152pub enum SMTSolverBuilderError {
153 NotInstalled(String),
155}
156
157impl fmt::Display for SMTSolverBuilderError {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
159 match self {
160 SMTSolverBuilderError::NotInstalled(s) => {
161 write!(f, "SMT solver {s} is not installed")
162 }
163 }
164 }
165}
166
167impl error::Error for SMTSolverBuilderError {}
168
169pub trait ProvidesSMTSolverBuilderCfg {
171 fn get_smt_solver_builder_cfg(&self) -> &SMTSolverBuilderCfg;
173}
174
175impl ProvidesSMTSolverBuilderCfg for SMTSolverBuilderCfg {
176 fn get_smt_solver_builder_cfg(&self) -> &SMTSolverBuilderCfg {
177 self
178 }
179}
180
181#[derive(Debug, Clone, PartialEq)]
186pub struct SMTSolverBuilder {
187 command: String,
188 args: Vec<String>,
189 opts: Vec<SMTSolverOption>,
190 set_lia: bool,
191}
192
193impl SMTSolverBuilder {
194 pub fn new(cfg: &SMTSolverBuilderCfg) -> Result<Self, SMTSolverBuilderError> {
196 let version = get_smt_solver_version(&cfg.command);
197 match version {
198 Ok(version) => {
199 trace!(
200 "SMT solver {} version {}.{}.{} found",
201 cfg.command, version.0, version.1, version.2
202 );
203
204 if let Some(check_version) = cfg.check_version {
205 check_version(version);
206 }
207 }
208 Err(e) => match e {
209 GetVersionError::NotInstalled(_) => {
210 return Err(SMTSolverBuilderError::NotInstalled(cfg.command.clone()));
211 }
212 GetVersionError::ParseVersionError => {
213 warn!("Failed to parse version of SMT solver {}", cfg.command);
214 }
215 },
216 }
217
218 Ok(Self {
219 command: cfg.command.clone(),
220 args: cfg.args.clone(),
221 opts: cfg.opts.clone(),
222 set_lia: cfg.set_lia,
223 })
224 }
225
226 pub fn new_solver(&self) -> SMTSolver {
228 trace!("Creating new solver instance of {}", self.command);
229 let mut builder = ContextBuilder::new();
230
231 if !self.command.is_empty() {
232 builder.solver(&self.command).solver_args(&self.args);
233 }
234
235 let mut solver = builder.build().unwrap_or_else(|_| {
236 panic!(
237 "Failed to start interactive session with SMT solver. Command: {}",
238 self.command
239 )
240 });
241
242 for opt in self.opts.iter() {
243 debug!("Applying SMT solver option {opt}");
244 opt.apply_option(&mut solver);
245 }
246
247 if self.set_lia {
248 debug!("Setting SMT solver logic to quantifier free LIA");
249 solver
250 .set_logic("LIA")
251 .expect("Failed to set logic `LIA` in the SMT solver.");
252 }
253
254 solver
255 }
256
257 pub fn new_solver_with_replay<W>(&mut self, replay_file: W) -> SMTSolver
260 where
261 W: Write + 'static + Send,
262 {
263 trace!("Creating new solver instance of {}", self.command);
264 let mut builder = ContextBuilder::new();
265 builder.replay_file(Some(replay_file));
266 builder.solver(&self.command).solver_args(&self.args);
267
268 let mut solver = builder.build().unwrap_or_else(|_| {
269 panic!(
270 "Failed to start interactive session with SMT solver. Command: {}",
271 self.command
272 )
273 });
274
275 for opt in self.opts.iter() {
276 opt.apply_option(&mut solver);
277 }
278
279 if self.set_lia {
280 debug!("Setting SMT solver logic to LIA");
281 solver
282 .set_logic("LIA")
283 .expect("Failed to set logic `LIA` in the SMT solver.");
284 }
285
286 solver
287 }
288
289 pub fn new_automatic_selection() -> Result<Self, SMTSolverBuilderError> {
294 if let Ok(z3) = SMTSolverBuilder::new(&SMTSolverBuilderCfg::new_z3()) {
295 Ok(z3)
296 } else if let Ok(cvc5) = SMTSolverBuilder::new(&SMTSolverBuilderCfg::new_cvc5()) {
297 Ok(cvc5)
298 } else {
299 Err(SMTSolverBuilderError::NotInstalled(
300 "No supported SMT solver found".to_string(),
301 ))
302 }
303 }
304}
305
306impl Default for SMTSolverBuilder {
307 fn default() -> Self {
310 SMTSolverBuilder::new_automatic_selection()
311 .expect("Failed to create default SMT solver builder. No SMT solver found.")
312 }
313}
314
315pub trait ProvidesSMTSolverBuilder {
322 fn get_solver_builder(&self) -> SMTSolverBuilder;
324}
325
326impl ProvidesSMTSolverBuilder for SMTSolverBuilder {
327 fn get_solver_builder(&self) -> SMTSolverBuilder {
328 self.clone()
329 }
330}
331
332#[derive(Debug, Clone, PartialEq)]
334#[cfg_attr(feature = "config_deserialize", derive(Deserialize))]
335pub enum SMTSolverOption {
336 BooleanOption {
338 name: String,
340 value: bool,
342 },
343 UnsignedIntOption {
345 name: String,
347 value: u32,
349 },
350}
351
352impl SMTSolverOption {
353 pub fn new_boolean_opt(name: String, value: bool) -> Self {
355 Self::BooleanOption { name, value }
356 }
357
358 pub fn new_integer_opt(name: String, value: u32) -> Self {
360 Self::UnsignedIntOption { name, value }
361 }
362
363 pub fn apply_option(&self, solver: &mut SMTSolver) {
365 match self {
366 SMTSolverOption::BooleanOption { name, value } => {
367 trace!("Setting SMT solver option {name} to {value}");
368 let value = if *value {
369 solver.true_()
370 } else {
371 solver.false_()
372 };
373
374 let res = solver.set_option(name, value);
375
376 if let Err(e) = res {
377 error!("Failed to set option {} in SMT solver ! Error: {}", name, e);
378 }
379 }
380 SMTSolverOption::UnsignedIntOption { name, value } => {
381 trace!("Setting SMT solver option {name} to {value}");
382 let value = solver.numeral(*value);
383 let res = solver.set_option(name, value);
384
385 if let Err(e) = res {
386 error!("Failed to set option {} in SMT solver ! Error: {}", name, e);
387 }
388 }
389 }
390 }
391}
392
393impl fmt::Display for SMTSolverOption {
394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
395 match self {
396 SMTSolverOption::BooleanOption { name, value } => write!(f, "{name} : {value}"),
397 SMTSolverOption::UnsignedIntOption { name, value } => write!(f, "{name} : {value}"),
398 }
399 }
400}
401
402pub trait SMTSolverContext {
409 fn get_smt_solver_mut(&mut self) -> &mut SMTSolver;
411
412 fn get_smt_solver(&self) -> &SMTSolver;
414
415 fn assert_and_check_expr(&mut self, expr: SMTExpr) -> Result<SMTSolution, SMTSolverError> {
417 self.get_smt_solver_mut().assert(expr)?;
418 let res = self.get_smt_solver_mut().check()?;
419
420 match res {
421 easy_smt::Response::Sat => Ok(SMTSolution::SAT),
422 easy_smt::Response::Unsat => Ok(SMTSolution::UNSAT),
423 easy_smt::Response::Unknown => Err(SMTSolverError::SolverTimeout),
424 }
425 }
426}
427
428impl SMTSolverContext for SMTSolver {
429 fn get_smt_solver_mut(&mut self) -> &mut SMTSolver {
430 self
431 }
432
433 fn get_smt_solver(&self) -> &SMTSolver {
434 self
435 }
436}
437
438#[derive(Debug, Clone, Copy, PartialEq)]
440pub enum SMTSolution {
441 UNSAT,
443 SAT,
445}
446
447impl SMTSolution {
448 pub fn is_sat(&self) -> bool {
450 match self {
451 SMTSolution::SAT => true,
452 SMTSolution::UNSAT => false,
453 }
454 }
455}
456
457impl fmt::Display for SMTSolution {
458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459 match self {
460 SMTSolution::UNSAT => write!(f, "UNSAT"),
461 SMTSolution::SAT => write!(f, "SAT"),
462 }
463 }
464}
465
466#[derive(Debug, PartialEq)]
468enum GetVersionError {
469 NotInstalled(String),
471 ParseVersionError,
473}
474
475impl fmt::Display for GetVersionError {
476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477 match self {
478 GetVersionError::NotInstalled(s) => write!(
479 f,
480 "Execution of `{s} --version` failed. This most likely means that the requested SMT solver is not installed."
481 ),
482 GetVersionError::ParseVersionError => write!(
483 f,
484 "Failed to parse version out of the output of `<SMT-SOLVER-COMMAND> --version`"
485 ),
486 }
487 }
488}
489
490impl error::Error for GetVersionError {}
491
492fn get_smt_solver_version(cmd: &str) -> Result<(i32, i32, i32), GetVersionError> {
494 let out = Command::new(cmd).arg("--version").output();
495 if out.is_err() {
496 return Err(GetVersionError::NotInstalled(cmd.to_owned()));
497 }
498 let out = out.unwrap();
499 if !out.status.success() {
500 return Err(GetVersionError::NotInstalled(cmd.to_owned()));
501 }
502
503 let out_str =
504 std::str::from_utf8(&out.stdout).map_err(|_| GetVersionError::ParseVersionError)?;
505 parse_smt_solver_version(out_str)
506}
507
508fn parse_smt_solver_version(version_output: &str) -> Result<(i32, i32, i32), GetVersionError> {
514 for token in version_output.split_whitespace() {
515 let parts: Vec<&str> = token.split('.').collect();
516 if parts.len() == 3
517 && let (Ok(major), Ok(minor), Ok(patch)) =
518 (parts[0].parse(), parts[1].parse(), parts[2].parse())
519 {
520 return Ok((major, minor, patch));
521 }
522 }
523
524 debug!("Failed to parse SMT solver version from output: {version_output}");
525 Err(GetVersionError::ParseVersionError)
526}
527
528#[cfg(test)]
529mod tests {
530 use std::vec;
531
532 use easy_smt::Response;
533
534 use super::*;
535
536 fn test_solver_interaction(solver: &mut SMTSolver) {
537 let int_sort = solver.int_sort();
538 let x = solver.declare_const("x", int_sort).unwrap();
539
540 let constr = solver.and(
541 solver.lte(x, solver.numeral(2)),
542 solver.gt(x, solver.numeral(1)),
543 );
544 solver.assert(constr).unwrap();
545
546 assert_eq!(solver.check().unwrap(), Response::Sat);
547
548 let solution = solver.get_value(vec![x]).unwrap();
549 assert_eq!(solution.len(), 1);
550 assert_eq!(solution[0].0, x);
551 assert_eq!(solver.get_u64(solution[0].1).unwrap(), 2);
552 }
553
554 fn test_solver_supports_negative_solutions(solver: &mut SMTSolver) {
556 let int_sort = solver.int_sort();
557 let z = solver.declare_const("z", int_sort).unwrap();
558
559 let constr = solver.and(
560 solver.lte(z, solver.numeral(-1)),
561 solver.gt(z, solver.numeral(-2)),
562 );
563 solver.assert(constr).unwrap();
564
565 assert_eq!(solver.check().unwrap(), Response::Sat);
566
567 let solution = solver.get_value(vec![z]).unwrap();
568 assert_eq!(solution.len(), 1);
569 assert_eq!(solution[0].0, z);
570 assert_eq!(solver.get_i64(solution[0].1).unwrap(), -1);
571 }
572
573 #[test]
574 fn test_z3_solver() {
575 let builder = SMTSolverBuilder::new(&SMTSolverBuilderCfg::new_z3());
576 assert!(builder.is_ok());
577 let builder = builder.unwrap();
578
579 let mut solver = builder.new_solver();
580 test_solver_interaction(&mut solver);
581 test_solver_supports_negative_solutions(&mut solver);
582 }
583
584 #[test]
585 fn test_cvc5_solver() {
586 let builder = SMTSolverBuilder::new(&SMTSolverBuilderCfg::new_cvc5());
587 assert!(builder.is_ok());
588 let builder = builder.unwrap();
589
590 let mut solver = builder.new_solver();
591 test_solver_interaction(&mut solver);
592 }
593
594 #[test]
595 fn test_cvc5_parse_version_1() {
596 let out = "\
597This is cvc5 version 1.1.0 [git tag 1.1.0 branch HEAD]
598compiled with GCC version 11.4.0
599on Dec 21 2023 01:08:41
600
601Copyright (c) 2009-2023 by the authors and their institutional
602affiliations listed at https://cvc5.github.io/people.html
603
604cvc5 is open-source and is covered by the BSD license (modified).
605
606THIS SOFTWARE IS PROVIDED AS-IS, WITHOUT ANY WARRANTIES.
607USE AT YOUR OWN RISK.
608
609This version of cvc5 is linked against the following non-(L)GPL'ed
610third party libraries.
611
612 CaDiCaL - Simplified Satisfiability Solver
613 See https://github.com/arminbiere/cadical for copyright information.
614
615 Editline Library
616 See https://thrysoee.dk/editline
617 for copyright information.
618
619 SymFPU - The Symbolic Floating Point Unit
620 See https://github.com/martin-cs/symfpu/tree/CVC4 for copyright information.
621
622This version of cvc5 is linked against the following third party
623libraries covered by the LGPLv3 license.
624See licenses/lgpl-3.0.txt for more information.
625
626 GMP - Gnu Multi Precision Arithmetic Library
627 See http://gmplib.org for copyright information.
628
629 LibPoly polynomial library
630 See https://github.com/SRI-CSL/libpoly for copyright and
631 licensing information.
632
633cvc5 is statically linked against these libraries. To recompile
634this version of cvc5 with different versions of these libraries
635follow the instructions on https://github.com/cvc5/cvc5/blob/main/INSTALL.md
636
637See the file COPYING (distributed with the source code, and with
638all binaries) for the full cvc5 copyright, licensing, and (lack of)
639warranty information.
640";
641
642 let got = parse_smt_solver_version(out);
643 assert_eq!(got, Ok((1, 1, 0)))
644 }
645
646 #[test]
647 fn test_cvc5_parse_version_2() {
648 let out = "\
649This is cvc5 version 1.2.0
650compiled with GCC version 14.2.1 20240912 (Red Hat 14.2.1-3)
651on Sep 26 2024 00:00:00
652
653Copyright (c) 2009-2023 by the authors and their institutional
654affiliations listed at https://cvc5.github.io/people.html
655
656This build of cvc5 uses GPLed libraries, and is thus covered by
657the GNU General Public License (GPL) version 3. Versions of cvc5
658are available that are covered by the (modified) BSD license. If
659you want to license cvc5 under this license, please configure cvc5
660with the \"--no-gpl\" option before building from sources.
661
662THIS SOFTWARE IS PROVIDED AS-IS, WITHOUT ANY WARRANTIES.
663USE AT YOUR OWN RISK.
664
665This version of cvc5 is linked against the following non-(L)GPL'ed
666third party libraries.
667
668 CaDiCaL - Simplified Satisfiability Solver
669 See https://github.com/arminbiere/cadical for copyright information.
670
671 CryptoMiniSat - An Advanced SAT Solver
672 See https://github.com/msoos/cryptominisat for copyright information.
673
674 Kissat - Simplified Satisfiability Solver
675 See https://fmv.jku.at/kissat for copyright information.
676
677 Editline Library
678 See https://thrysoee.dk/editline
679 for copyright information.
680
681 SymFPU - The Symbolic Floating Point Unit
682 See https://github.com/martin-cs/symfpu/tree/CVC4 for copyright information.
683
684This version of cvc5 is linked against the following third party
685libraries covered by the LGPLv3 license.
686See licenses/lgpl-3.0.txt for more information.
687
688 GMP - Gnu Multi Precision Arithmetic Library
689 See http://gmplib.org for copyright information.
690
691 LibPoly polynomial library
692 See https://github.com/SRI-CSL/libpoly for copyright and
693 licensing information.
694
695This version of cvc5 is linked against the following third party
696libraries covered by the GPLv3 license.
697See licenses/gpl-3.0.txt for more information.
698
699 CoCoALib - a computer algebra library
700 See https://cocoa.dima.unige.it/cocoa/cocoalib/index.shtml for copyright information
701
702See the file COPYING (distributed with the source code, and with
703all binaries) for the full cvc5 copyright, licensing, and (lack of)
704warranty information.
705";
706
707 let got = parse_smt_solver_version(out);
708 assert_eq!(got, Ok((1, 2, 0)))
709 }
710
711 #[test]
712 fn test_cvc5_parse_version_3() {
713 let out = "\
714Could not read history from /root/.cvc5_history_smtlib2: No such file or directory
715cvc5 1.4.0 [git b432cd7 on branch HEAD]
716compiled as a unrestricted build with GCC version 11.4.0 on Sep 17 2026 21:14:31
717
718Copyright (c) 2009-2026 by the authors and their institutional
719affiliations listed at https://cvc5.github.io/people.html
720
721cvc5 is open-source and is covered by the BSD license (modified).
722
723THIS SOFTWARE IS PROVIDED AS-IS, WITHOUT ANY WARRANTIES.
724USE AT YOUR OWN RISK.
725
726This version of cvc5 is linked against the following non-(L)GPL'ed
727third party libraries.
728
729 CaDiCaL - Simplified Satisfiability Solver
730 See https://github.com/arminbiere/cadical for copyright information.
731
732 Editline Library
733 See https://thrysoee.dk/editline
734 for copyright information.
735
736 SymFPU - The Symbolic Floating Point Unit
737 See https://github.com/martin-cs/symfpu/tree/main for copyright information.
738
739This version of cvc5 is linked against the following third party
740libraries covered by the LGPLv3 license.
741See licenses/lgpl-3.0.txt for more information.
742
743 GMP - Gnu Multi Precision Arithmetic Library
744 See http://gmplib.org for copyright information.
745
746 LibPoly polynomial library
747 See https://github.com/SRI-CSL/libpoly for copyright and
748 licensing information.
749
750cvc5 is statically linked against these libraries. To recompile
751this version of cvc5 with different versions of these libraries
752follow the instructions on https://github.com/cvc5/cvc5/blob/main/INSTALL.md
753
754See the file COPYING (distributed with the source code, and with
755all binaries) for the full cvc5 copyright, licensing, and (lack of)
756warranty information.
757";
758
759 let got = parse_smt_solver_version(out);
760 assert_eq!(got, Ok((1, 4, 0)))
761 }
762
763 #[test]
764 fn test_z3_parse_version() {
765 let out = "\
766Z3 version 4.8.12 - 64 bit";
767
768 let got = parse_smt_solver_version(out);
769 assert_eq!(got, Ok((4, 8, 12)))
770 }
771
772 #[test]
773 fn test_z3_get_version() {
774 let version = get_smt_solver_version(Z3_PRG);
775 assert!(version.is_ok());
776 }
777
778 #[test]
779 fn test_cvc5_get_version() {
780 let version = get_smt_solver_version(CVC5_PRG);
781 assert!(version.is_ok());
782 }
783
784 #[test]
785 fn test_automatic_solver_selection() {
786 let builder = SMTSolverBuilder::new_automatic_selection()
787 .expect("Failed to create default SMT solver builder. No SMT solver found.");
788
789 let mut solver = builder.new_solver();
790 test_solver_interaction(&mut solver);
791 }
792
793 #[test]
794 fn test_command_solver() {
795 let cvc5_args: Vec<String> = CVC5_ARGS.iter().map(|&s| s.to_string()).collect();
796
797 let cfg = SMTSolverBuilderCfg::new("cvc5".to_string(), cvc5_args.clone(), vec![], false);
798 let builder = SMTSolverBuilder::new(&cfg).unwrap();
799
800 let mut solver = builder.new_solver();
801 test_solver_interaction(&mut solver);
802
803 let second_builder = builder.clone();
804 assert_eq!(builder, second_builder);
805 }
806
807 #[test]
808 fn test_command_solver_set_lia() {
809 let cvc5_args: Vec<String> = CVC5_ARGS.iter().map(|&s| s.to_string()).collect();
810
811 let cfg = SMTSolverBuilderCfg::new("cvc5".to_string(), cvc5_args.clone(), vec![], true);
812 let builder = SMTSolverBuilder::new(&cfg).unwrap();
813
814 let mut solver = builder.new_solver();
815 test_solver_interaction(&mut solver);
816
817 let second_builder = builder.clone();
818 assert_eq!(builder, second_builder);
819 }
820
821 #[test]
822 #[should_panic]
823 fn test_panic_unknown_solver() {
824 let cfg = SMTSolverBuilderCfg::new("unknown_solver".to_string(), vec![], vec![], false);
825 let _builder = SMTSolverBuilder::new(&cfg).unwrap();
826 }
827
828 #[test]
829 fn display_solution() {
830 assert_eq!(SMTSolution::UNSAT.to_string(), "UNSAT");
831 assert_eq!(SMTSolution::SAT.to_string(), "SAT");
832 }
833
834 #[test]
835 fn test_is_sat() {
836 assert!(!SMTSolution::UNSAT.is_sat());
837 assert!(SMTSolution::SAT.is_sat());
838 }
839
840 #[test]
841 fn test_get_version_error_display() {
842 let err = GetVersionError::NotInstalled("cvc5".to_string());
843 assert_eq!(
844 err.to_string(),
845 "Execution of `cvc5 --version` failed. This most likely means that the requested SMT solver is not installed."
846 );
847 let err = GetVersionError::ParseVersionError;
848 assert_eq!(
849 err.to_string(),
850 "Failed to parse version out of the output of `<SMT-SOLVER-COMMAND> --version`"
851 );
852 let err = GetVersionError::ParseVersionError;
853 assert_eq!(
854 err.to_string(),
855 "Failed to parse version out of the output of `<SMT-SOLVER-COMMAND> --version`"
856 );
857 }
858
859 #[test]
860 fn test_solver_option_display() {
861 let opt = SMTSolverOption::BooleanOption {
862 name: ":opt".to_string(),
863 value: true,
864 };
865 assert_eq!(opt.to_string(), ":opt : true");
866 let opt = SMTSolverOption::UnsignedIntOption {
867 name: ":opt".to_string(),
868 value: 42,
869 };
870 assert_eq!(opt.to_string(), ":opt : 42");
871 }
872
873 #[test]
874 fn test_new_solver_opt() {
875 let opt = SMTSolverOption::new_boolean_opt(":opt".to_string(), true);
876 assert_eq!(
877 opt,
878 SMTSolverOption::BooleanOption {
879 name: ":opt".to_string(),
880 value: true,
881 }
882 );
883 let opt = SMTSolverOption::new_integer_opt(":opt".to_string(), 42);
884 assert_eq!(
885 opt,
886 SMTSolverOption::UnsignedIntOption {
887 name: ":opt".to_string(),
888 value: 42,
889 }
890 );
891 }
892
893 #[test]
894 fn test_fmt_solver_builder_error() {
895 let err = SMTSolverBuilderError::NotInstalled("cvc5".to_string());
896 assert_eq!(err.to_string(), "SMT solver cvc5 is not installed");
897 }
898
899 #[test]
900 fn test_eq_smt_solver_builder_cfg() {
901 let cfg1 = SMTSolverBuilderCfg::new(
902 "cvc5".to_string(),
903 vec!["--quiet".to_string(), "--lang=smt2".to_string()],
904 vec![],
905 false,
906 );
907 let cfg2 = SMTSolverBuilderCfg::new(
908 "cvc5".to_string(),
909 vec!["--quiet".to_string(), "--lang=smt2".to_string()],
910 vec![],
911 false,
912 );
913 assert_eq!(cfg1, cfg2);
914
915 let cfg3 = SMTSolverBuilderCfg::new(
916 "z3".to_string(),
917 vec!["-smt2".to_string(), "-in".to_string(), "-v:0".to_string()],
918 vec![],
919 false,
920 );
921 assert_ne!(cfg1, cfg3);
922 }
923}