use std::io::Cursor;
use imodfile::*;
use proptest::prelude::*;
fn any_point() -> impl Strategy<Value = Ipoint> {
(-1000.0f32..=1000.0, -1000.0f32..=1000.0, -1000.0f32..=1000.0)
.prop_map(|(x, y, z)| Ipoint { x, y, z })
}
fn valid_contour() -> impl Strategy<Value = Icont> {
(
proptest::collection::vec(any_point(), 0..=10),
any::<u32>(),
any::<i32>(),
any::<i32>(),
)
.prop_map(|(pts, flags, time, surf)| {
let psize = pts.len() as i32;
Icont {
pts,
psize,
flags,
time,
surf,
..Icont::default()
}
})
}
fn valid_object() -> impl Strategy<Value = Iobj> {
(
"[A-Za-z0-9_ ]{0,32}",
proptest::collection::vec(valid_contour(), 0..=5),
any::<u32>(),
0.0f32..=1.0,
0.0f32..=1.0,
0.0f32..=1.0,
)
.prop_map(|(name, cont, flags, red, green, blue)| {
let contsize = cont.len() as i32;
Iobj {
name,
cont,
contsize,
flags,
red,
green,
blue,
..Iobj::default()
}
})
}
fn valid_model() -> impl Strategy<Value = Imod> {
(
"[A-Za-z0-9_ ]{0,32}",
proptest::collection::vec(valid_object(), 0..=5),
any::<u32>(),
-1000i32..=1000,
-1000i32..=1000,
-1000i32..=1000,
)
.prop_map(|(name, obj, flags, xmax, ymax, zmax)| {
let objsize = obj.len() as i32;
Imod {
name,
obj,
objsize,
flags,
xmax,
ymax,
zmax,
..Imod::default()
}
})
}
proptest! {
#[test]
fn binary_roundtrip(model in valid_model()) {
let mut buf = Vec::new();
let mut cursor = Cursor::new(&mut buf);
prop_assert!(write_binary(&mut cursor, &model).is_ok());
cursor.set_position(0);
let model2 = read_binary(&mut cursor).expect("read_binary failed");
prop_assert_eq!(&model.name, &model2.name);
prop_assert_eq!(model.objsize, model2.objsize);
prop_assert_eq!(model.xmax, model2.xmax);
prop_assert_eq!(model.ymax, model2.ymax);
prop_assert_eq!(model.zmax, model2.zmax);
let expected_flags = model.flags
| IMODF_MAT1_IS_BYTES
| IMODF_MULTIPLE_CLIP
| IMODF_HAS_MESH_THICK;
prop_assert_eq!(expected_flags, model2.flags);
prop_assert_eq!(model.obj.len(), model2.obj.len());
for (oi, (o1, o2)) in model.obj.iter().zip(model2.obj.iter()).enumerate() {
prop_assert_eq!(&o1.name, &o2.name, "obj {} name mismatch", oi);
prop_assert_eq!(o1.contsize, o2.contsize, "obj {} contsize mismatch", oi);
prop_assert_eq!(o1.red, o2.red, "obj {} red mismatch", oi);
prop_assert_eq!(o1.green, o2.green, "obj {} green mismatch", oi);
prop_assert_eq!(o1.blue, o2.blue, "obj {} blue mismatch", oi);
prop_assert_eq!(o1.flags, o2.flags, "obj {} flags mismatch", oi);
prop_assert_eq!(o1.cont.len(), o2.cont.len(), "obj {} cont len mismatch", oi);
for (ci, (c1, c2)) in o1.cont.iter().zip(o2.cont.iter()).enumerate() {
prop_assert_eq!(c1.psize, c2.psize, "obj {} cont {} psize mismatch", oi, ci);
prop_assert_eq!(c1.surf, c2.surf, "obj {} cont {} surf mismatch", oi, ci);
prop_assert_eq!(c1.flags, c2.flags, "obj {} cont {} flags mismatch", oi, ci);
prop_assert_eq!(c1.time, c2.time, "obj {} cont {} time mismatch", oi, ci);
prop_assert_eq!(c1.pts.len(), c2.pts.len(), "obj {} cont {} pts len mismatch", oi, ci);
for (pi, (p1, p2)) in c1.pts.iter().zip(c2.pts.iter()).enumerate() {
let tol = 1.0e-5;
prop_assert!(
(p1.x - p2.x).abs() < tol,
"obj {} cont {} pt {} x mismatch: {} != {}", oi, ci, pi, p1.x, p2.x
);
prop_assert!(
(p1.y - p2.y).abs() < tol,
"obj {} cont {} pt {} y mismatch: {} != {}", oi, ci, pi, p1.y, p2.y
);
prop_assert!(
(p1.z - p2.z).abs() < tol,
"obj {} cont {} pt {} z mismatch: {} != {}", oi, ci, pi, p1.z, p2.z
);
}
}
}
}
#[test]
fn point_size_consistency(model in valid_model()) {
for (oi, obj) in model.obj.iter().enumerate() {
for (ci, cont) in obj.cont.iter().enumerate() {
prop_assert_eq!(
cont.psize as usize,
cont.pts.len(),
"obj {} cont {}: psize {} != pts.len() {}",
oi, ci, cont.psize, cont.pts.len()
);
}
}
}
}