use super::{
LoadedFingerprints,
convert::CandidateSource,
dto::DesignShape,
fixtures::{plain_block, sample_layout, settings},
format::{design_shape, parse_and_validate_plan},
save::{LibraryId, snapshot_of, stored_shapes_and_library, write_plan},
store,
tests_db::{add_design, library},
};
use crate::gui::rough_plan::run::{ResultsSource, RunState};
use indicatrix_vault::{db::sqlite::Database, model::solid_extents::SolidExtents};
use std::{collections::BTreeMap, sync::Mutex};
fn planned_extents(width: f64) -> SolidExtents {
SolidExtents {
width_caliper: width,
length_caliper: 1.2 * width,
width_axis: width,
length_axis: 1.2 * width,
height: 0.5 * width,
volume: 0.6 * width * width * width,
}
}
fn planned_shapes() -> BTreeMap<i64, DesignShape> {
BTreeMap::from([
(1, design_shape(&planned_extents(2.0))),
(2, design_shape(&planned_extents(2.5))),
])
}
fn today_shape(db: &Mutex<Database>, id: i64) -> DesignShape {
let stored = db
.lock()
.expect("lock")
.solid_extents_for(&[id])
.expect("extents");
design_shape(&stored[&id].extents.expect("measured"))
}
fn library_today() -> Mutex<Database> {
let db = library();
assert_eq!(add_design(&db, "Barion Oval", 3.0), 1);
assert_eq!(add_design(&db, "Emerald", 4.0), 2);
db
}
fn results_on_screen(shapes: BTreeMap<i64, DesignShape>, source: ResultsSource) -> RunState {
let mut run = RunState::default();
show_fixture(&mut run, shapes, source);
run
}
fn show_fixture(run: &mut RunState, shapes: BTreeMap<i64, DesignShape>, source: ResultsSource) {
run.layouts = vec![sample_layout()];
run.plan_model = Some(plain_block());
run.plan_settings = Some(settings());
run.material_name = "Aquamarine".to_string();
run.weighed_ct = Some(8.9);
run.candidate_source = CandidateSource::Library;
run.titles = BTreeMap::from([(1, "Barion Oval".to_string()), (2, "Emerald".to_string())]);
run.shapes = shapes;
run.keep = vec![false];
run.source = source;
}
fn save_and_reload(
db: &Mutex<Database>,
run: &RunState,
loaded: Option<&LoadedFingerprints>,
) -> super::format::LoadedPlan {
let snapshot = snapshot_of(run, loaded, false).expect("there are results to save");
write_plan(db, "Saved", &snapshot).expect("the plan is saved");
let listed = store::list_saved(db).expect("listed");
let stored = store::load_saved(db, listed[0].id).expect("loaded");
parse_and_validate_plan(&stored.payload).expect("the saved plan parses")
}
fn width_of(shape: &DesignShape) -> f64 {
shape.width_caliper.expect("the shape has a width")
}
#[test]
fn a_fresh_plan_is_saved_with_the_shapes_it_was_planned_for_not_todays() {
let db = library_today();
let planned = planned_shapes();
for id in [1, 2] {
assert_ne!(
planned[&id],
today_shape(&db, id),
"the library was rescaled after planning"
);
}
let run = results_on_screen(planned.clone(), ResultsSource::Planned);
let plan = save_and_reload(&db, &run, None);
for id in [1, 2] {
let design = plan
.designs
.iter()
.find(|design| design.entry_id == id)
.expect("the design is recorded");
assert_eq!(
design.shape(),
planned[&id],
"design {id}: the planned shape"
);
assert_ne!(
design.shape(),
today_shape(&db, id),
"design {id}: not today's"
);
}
let widths: Vec<f64> = plan.designs.iter().map(|d| width_of(&d.shape())).collect();
assert!((widths[0] - 2.0).abs() < 1e-8, "{widths:?}");
assert!((widths[1] - 2.5).abs() < 1e-8, "{widths:?}");
assert!(plan.library_id.is_some());
assert_eq!(
plan.library_id,
db.lock().expect("lock").library_stamp().ok()
);
}
#[test]
fn a_design_without_a_planned_shape_is_measured_from_the_library() {
let db = library_today();
let shapes = BTreeMap::from([(1, design_shape(&planned_extents(2.0)))]);
let run = results_on_screen(shapes.clone(), ResultsSource::Planned);
let plan = save_and_reload(&db, &run, None);
let shape_of = |id: i64| {
plan.designs
.iter()
.find(|design| design.entry_id == id)
.expect("the design is recorded")
.shape()
};
assert_eq!(shape_of(1), shapes[&1]);
assert_eq!(shape_of(2), today_shape(&db, 2));
}
#[test]
fn a_loaded_plan_saved_again_keeps_its_stored_shapes_and_its_library() {
let db = library_today();
let stored = planned_shapes();
let source = ResultsSource::Loaded {
plan_id: 7,
name: "Aqua".to_string(),
created_at: 0,
};
let run = results_on_screen(BTreeMap::new(), source);
let loaded = LoadedFingerprints {
plan_id: 7,
library_id: Some(5),
shapes: stored.clone(),
};
let plan = save_and_reload(&db, &run, Some(&loaded));
for id in [1, 2] {
let design = plan
.designs
.iter()
.find(|design| design.entry_id == id)
.expect("the design is recorded");
assert_eq!(design.shape(), stored[&id], "design {id}: the file's shape");
}
assert_eq!(
plan.library_id,
Some(5),
"the ids still belong to the library the file came from"
);
}
#[test]
fn the_stored_shapes_come_from_the_loaded_plan_only_while_that_plan_is_on_screen() {
let sized = |width: f64| DesignShape {
width_caliper: Some(width),
..DesignShape::default()
};
let planned = BTreeMap::from([(1, sized(1.0))]);
let file = BTreeMap::from([(1, sized(2.0))]);
let loaded = LoadedFingerprints {
plan_id: 7,
library_id: None,
shapes: file.clone(),
};
let shown = |plan_id: i64| ResultsSource::Loaded {
plan_id,
name: String::new(),
created_at: 0,
};
let run = results_on_screen(planned.clone(), shown(7));
assert_eq!(
stored_shapes_and_library(&run, Some(&loaded)),
(file, LibraryId::Kept(None))
);
let other = results_on_screen(planned.clone(), shown(8));
assert_eq!(
stored_shapes_and_library(&other, Some(&loaded)),
(planned.clone(), LibraryId::Local)
);
let fresh = results_on_screen(planned.clone(), ResultsSource::Planned);
assert_eq!(
stored_shapes_and_library(&fresh, Some(&loaded)),
(planned.clone(), LibraryId::Local)
);
assert_eq!(
stored_shapes_and_library(&fresh, None),
(planned, LibraryId::Local)
);
}