#![allow(clippy::unwrap_used)]
#![allow(clippy::default_trait_access)]
#![allow(unused_imports)]
use std::fmt::Write as _;
use std::io::Cursor;
use cadmpeg_core::decode::{DecodeMode, InspectOptions};
use cadmpeg_ir::codec::{Codec, CodecBackend, Confidence, DecodeOptions};
use cadmpeg_ir::eval::{
model_curve_point_by_id, model_surface_partials_by_id, model_surface_point_by_id, pcurve_uv,
};
use cadmpeg_ir::examples::unit_cube;
use cadmpeg_ir::geometry::{
Curve, CurveGeometry, NurbsCurve, NurbsSurface, PcurveGeometry, Surface, SurfaceGeometry,
};
use cadmpeg_ir::ids::{CurveId, ProceduralCurveId, SurfaceId};
use cadmpeg_ir::index::ModelIndex;
use cadmpeg_ir::math::{Point2, Point3, Vector3};
use cadmpeg_ir::transform::Transform;
use cadmpeg_ir::units::{LengthUnit, Units};
use cadmpeg_ir::CadIr;
use zip::write::SimpleFileOptions;
use zip::{CompressionMethod, ZipWriter};
use crate::ids::StepIdentity;
use crate::loss::StepLossCode;
use crate::test_support::{decode_inline, export};
use crate::{
write_step, StepCodec, StepError, StepSchema, StepUnsupportedPolicy, StepWriteOptions,
};
#[test]
fn parser_rejects_duplicate_complex_partial_names() {
let source = b"ISO-10303-21;HEADER;FILE_DESCRIPTION(('test'),'2;1');FILE_NAME('','',(''),(''),'','','');FILE_SCHEMA(('AP242'));ENDSEC;DATA;#1=(B()A()B());ENDSEC;END-ISO-10303-21;";
let error = crate::parse::parse(source).expect_err("duplicate partial names must fail");
assert!(matches!(
error,
crate::parse::ParseError::Syntax { message, .. }
if message == "duplicate complex partial name"
));
}
#[test]
fn parser_reports_recoverable_noncanonical_complex_partial_order() {
let source = b"ISO-10303-21;HEADER;FILE_DESCRIPTION(('test'),'2;1');FILE_NAME('','',(''),(''),'','','');FILE_SCHEMA(('AP242'));ENDSEC;DATA;#1=(NAMED_UNIT(#2)SOLID_ANGLE_UNIT()SI_UNIT($,.STERADIAN.));#2=DIMENSIONAL_EXPONENTS(0.,0.,0.,0.,0.,0.,0.);ENDSEC;END-ISO-10303-21;";
let (exchange, diagnostics) =
crate::parse::parse(source).expect("noncanonical partial order is recoverable");
assert_eq!(diagnostics.len(), 1);
assert_eq!(
diagnostics[0].offset,
source
.windows(2)
.position(|window| window == b"#1")
.unwrap()
);
assert_eq!(
diagnostics[0].kind,
crate::parse::ParseDiagnosticKind::ComplexPartialsNotAlphabetical
);
assert_eq!(
diagnostics[0].message,
"complex partial records are not alphabetical: observed (NAMED_UNIT, SOLID_ANGLE_UNIT, SI_UNIT), expected (NAMED_UNIT, SI_UNIT, SOLID_ANGLE_UNIT)"
);
assert_eq!(
exchange.records[&1]
.partials
.iter()
.map(|partial| partial.name.as_str())
.collect::<Vec<_>>(),
["NAMED_UNIT", "SOLID_ANGLE_UNIT", "SI_UNIT"]
);
}
#[test]
fn decode_salvages_noncanonical_complex_partial_order_with_provenance() {
let bytes = include_bytes!("../../../tests/fixtures/noncanonical_solid_angle.p21");
let result = StepCodec::default()
.decode(&mut Cursor::new(bytes), &DecodeOptions::default())
.expect("salvage mode accepts recoverable source order");
let losses = result
.report()
.losses
.iter()
.filter(|loss| loss.code == StepLossCode::ParseNoncanonicalSyntax.kind())
.collect::<Vec<_>>();
assert_eq!(losses.len(), 1);
assert_eq!(losses[0].severity, cadmpeg_ir::Severity::Warning);
let provenance = losses[0].provenance.as_ref().expect("source provenance");
assert_eq!(provenance.format, "step");
assert_eq!(provenance.stream, "");
assert_eq!(
provenance.offset,
bytes.windows(2).position(|window| window == b"#1").unwrap() as u64
);
assert_eq!(provenance.tag.as_deref(), Some("complex_entity"));
assert_eq!(result.ir().native_unknowns("step").unwrap().len(), 0);
assert_eq!(
result.ir().source.as_ref().unwrap().attributes["bytes_named_opaque"],
"0"
);
}
#[test]
fn strict_decode_rejects_noncanonical_complex_partial_order() {
let bytes = include_bytes!("../../../tests/fixtures/noncanonical_solid_angle.p21");
let mut options = DecodeOptions::default();
options.policy.mode = DecodeMode::Strict;
let error = StepCodec::default()
.decode(&mut Cursor::new(bytes), &options)
.expect_err("strict mode rejects noncanonical source order");
assert!(matches!(error, cadmpeg_core::CodecError::Malformed(_)));
}
#[test]
fn inspect_accepts_noncanonical_complex_partial_order_and_reports_a_note() {
let bytes = include_bytes!("../../../tests/fixtures/noncanonical_solid_angle.p21");
let summary = StepCodec::default()
.inspect(&mut Cursor::new(bytes), &InspectOptions::default())
.expect("inspection describes recoverable source order");
assert!(summary
.notes
.iter()
.any(|note| note.contains("complex partial records are not alphabetical")));
}
#[test]
fn exporting_a_salvaged_noncanonical_unit_repairs_partial_order() {
let bytes = include_bytes!("../../../tests/fixtures/noncanonical_solid_angle.p21");
let decoded = StepCodec::default()
.decode(&mut Cursor::new(bytes), &DecodeOptions::default())
.expect("decode noncanonical unit fixture");
let mut output = Vec::new();
write_step(decoded.ir(), &mut output, &StepWriteOptions::default())
.expect("export salvaged IR");
let (exchange, diagnostics) = crate::parse::parse(&output).expect("parse repaired output");
assert!(diagnostics.is_empty());
let unit = exchange
.records
.values()
.find(|record| {
record
.partials
.iter()
.any(|partial| partial.name == "SOLID_ANGLE_UNIT")
})
.expect("exported solid-angle unit");
assert_eq!(
unit.partials
.iter()
.map(|partial| partial.name.as_str())
.collect::<Vec<_>>(),
["NAMED_UNIT", "SI_UNIT", "SOLID_ANGLE_UNIT"]
);
}