use approx::assert_abs_diff_eq;
use fts_solver::{
PortfolioOutcome, ProductOutcome,
io::{Auction, DemandId, Outcome, PortfolioId, ProductId},
};
use rstest::*;
use rstest_reuse::{self, *};
use serde::de::DeserializeOwned;
use std::{
fs::File,
io::{BufReader, Read},
path::PathBuf,
};
#[template]
#[rstest]
#[case::clarabel(fts_solver::clarabel::ClarabelSolver::default())]
#[case::osqp(fts_solver::osqp::OsqpSolver::default())]
pub fn all_solvers<PortfolioId, ProductId>(
#[case] solver: impl solver::Solver<
PortfolioOutcome = fts_solver::PortfolioOutcome,
ProductOutcome = fts_solver::ProductOutcome,
>,
) -> () {
}
#[apply(all_solvers)]
#[rstest]
#[tokio::test]
async fn run_auction<
T: fts_core::ports::Solver<
DemandId,
PortfolioId,
ProductId,
PortfolioOutcome = fts_solver::PortfolioOutcome,
ProductOutcome = fts_solver::ProductOutcome,
>,
>(
solver: T,
#[files("tests/samples/**/output.json")] output: PathBuf,
) where
T::PortfolioOutcome: DeserializeOwned,
T::ProductOutcome: DeserializeOwned,
{
use fts_solver::io::Outcome;
let mut input = output.clone();
input.set_file_name("input.json");
let auction: Auction =
serde_json::from_reader(BufReader::new(File::open(input).unwrap())).unwrap();
let reference: Outcome<T::PortfolioOutcome, T::ProductOutcome> =
serde_json::from_reader(BufReader::new(File::open(output).unwrap())).unwrap();
let solution = auction.solve(solver).await;
cmp(&solution, &reference, 1e-6, 1e-6);
}
#[rstest]
fn check_mps_export(#[files("tests/samples/**/export.mps")] output: PathBuf) {
let mut input = output.clone();
input.set_file_name("input.json");
let auction: Auction =
serde_json::from_reader(BufReader::new(File::open(input).unwrap())).unwrap();
let mut output_bytes = Vec::new();
let output_size = File::open(output)
.unwrap()
.read_to_end(&mut output_bytes)
.unwrap();
let mut export_bytes = Vec::with_capacity(output_size);
auction.export_mps(&mut export_bytes).unwrap();
assert!(output_bytes == export_bytes, "mps files are not identical");
}
#[rstest]
fn check_lp_export(#[files("tests/samples/**/export.lp")] output: PathBuf) {
let mut input = output.clone();
input.set_file_name("input.json");
let auction: Auction =
serde_json::from_reader(BufReader::new(File::open(input).unwrap())).unwrap();
let mut output_bytes = Vec::new();
let output_size = File::open(output)
.unwrap()
.read_to_end(&mut output_bytes)
.unwrap();
let mut export_bytes = Vec::with_capacity(output_size);
auction.export_lp(&mut export_bytes).unwrap();
assert!(output_bytes == export_bytes, "mps files are not identical");
}
fn cmp(
a: &Outcome<PortfolioOutcome, ProductOutcome>,
b: &Outcome<PortfolioOutcome, ProductOutcome>,
qeps: f64,
peps: f64,
) {
assert_eq!(a.products.len(), b.products.len());
for ((p1, o1), (p2, o2)) in a.products.iter().zip(b.products.iter()) {
assert_eq!(p1, p2);
assert_abs_diff_eq!(o1.rate, o2.rate, epsilon = qeps);
assert_abs_diff_eq!(o1.price, o2.price, epsilon = peps);
}
assert_eq!(a.portfolios.len(), b.portfolios.len());
for ((p1, o1), (p2, o2)) in a.portfolios.iter().zip(b.portfolios.iter()) {
assert_eq!(p1, p2);
assert_abs_diff_eq!(o1.rate, o2.rate, epsilon = qeps);
assert_abs_diff_eq!(o1.price, o2.price, epsilon = peps);
}
}