use std::process::Command;
fn dependency_tree(extra: &[&str]) -> String {
let manifest = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml");
let mut cmd = Command::new(env!("CARGO"));
cmd.args([
"tree",
"--manifest-path",
manifest,
"--edges",
"normal",
"--prefix",
"none",
]);
cmd.args(extra);
let out = cmd.output().expect("cargo tree should run");
assert!(
out.status.success(),
"cargo tree failed: {}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8(out.stdout).expect("tree output is utf-8")
}
fn links(tree: &str, crate_name: &str) -> bool {
tree.lines()
.filter_map(|line| line.split_whitespace().next())
.any(|name| name == crate_name)
}
const KERNEL_CRATES: &[&str] = &[
"axiolid-core",
"axiolid-curve",
"axiolid-mesh",
"axiolid-model",
"axiolid-primitive",
"axiolid-profile",
"axiolid-surface",
"axiolid-topology",
"glam",
];
#[test]
fn the_kernel_free_build_links_no_geometry_crate() {
let tree = dependency_tree(&["--no-default-features"]);
for forbidden in KERNEL_CRATES {
assert!(
!links(&tree, forbidden),
"a kernel-free build links {forbidden}. Something outside `lower` \
uses the neutral vocabulary unconditionally; gate it behind \
`#[cfg(feature = \"lowering\")]`.\n{tree}"
);
}
assert!(links(&tree, "ifc-model"), "the model is not optional");
assert!(
links(&tree, "ifc-schema"),
"schema queries are not optional"
);
}
#[test]
fn the_default_build_still_links_the_neutral_vocabulary() {
let tree = dependency_tree(&[]);
for required in ["axiolid-core", "axiolid-model"] {
assert!(
links(&tree, required),
"the default build lost {required}; lowering cannot work.\n{tree}"
);
}
}
#[test]
fn the_execution_provider_is_opt_in_only() {
const PROVIDER: &[&str] = &[
"axiolid-mesh-compile",
"axiolid-mesh-boolean-boolmesh",
"axiolid-construct",
];
for column in [vec!["--no-default-features"], vec![]] {
let tree = dependency_tree(&column);
for crate_name in PROVIDER {
assert!(
!links(&tree, crate_name),
"{column:?} links execution provider {crate_name}; compilation must stay opt-in"
);
}
}
let compiled = dependency_tree(&["--features", "compile-reference-backend"]);
for crate_name in PROVIDER {
assert!(
links(&compiled, crate_name),
"--features compile-reference-backend lost {crate_name}; the feature cannot work"
);
}
}
#[test]
#[cfg(not(feature = "lowering"))]
fn placement_resolution_survives_without_the_kernel() {
let resolve: fn(
&ifc_model::Model,
&ifc_geometry::units::UnitScale,
ifc_model::EntityId,
) -> ifc_geometry::GeometryResult<ifc_geometry::Transform> =
ifc_geometry::product_world_transform;
let model = ifc_model::Model::new();
let units = ifc_geometry::units::UnitScale::default();
assert!(
resolve(&model, &units, ifc_model::EntityId(1)).is_err(),
"an absent product must report rather than silently resolve to origin"
);
}
#[test]
#[cfg(not(feature = "lowering"))]
fn authoring_survives_without_the_kernel() {
use ifc_model::{Model, Transaction};
let model = Model::new();
let mut tx = Transaction::new(&model);
let origin = ifc_geometry::authoring::cartesian_point(&mut tx, &[0.0, 0.0, 0.0])
.expect("a point needs no kernel");
let up = ifc_geometry::authoring::direction(&mut tx, &[0.0, 0.0, 1.0])
.expect("a direction needs no kernel");
let place = ifc_geometry::authoring::axis2_placement_3d(&mut tx, origin, Some(up), None);
let profile = ifc_geometry::authoring::rectangle_profile(&mut tx, None, None, 6.0, 0.3)
.expect("a profile needs no kernel");
let solid =
ifc_geometry::authoring::extruded_area_solid(&mut tx, profile, Some(place), up, 2.4)
.expect("an extrusion is a description, not an evaluation");
let mut model = model;
tx.commit(&mut model).expect("commit");
assert_eq!(
model.get(solid).expect("solid present").type_name.as_ref(),
"IFCEXTRUDEDAREASOLID"
);
}
#[test]
#[cfg(not(feature = "lowering"))]
fn body_description_survives_without_the_kernel() {
type Describe = fn(
&ifc_model::Model,
&ifc_geometry::units::UnitScale,
ifc_model::EntityId,
) -> ifc_geometry::GeometryResult<Option<ifc_geometry::BodyDescription>>;
type Profile = fn(
&ifc_model::Model,
&ifc_geometry::units::UnitScale,
ifc_model::EntityId,
) -> ifc_geometry::GeometryResult<ifc_geometry::ProfileDescription>;
let describe: Describe = ifc_geometry::body_description;
let profile: Profile = ifc_geometry::describe_profile;
let model = ifc_model::Model::new();
let units = ifc_geometry::units::UnitScale::default();
assert!(describe(&model, &units, ifc_model::EntityId(1)).is_err());
assert!(profile(&model, &units, ifc_model::EntityId(1)).is_err());
}
#[test]
fn the_kernel_free_build_links_no_alignment_bridge() {
let tree = dependency_tree(&["--no-default-features"]);
assert!(
!links(&tree, "ifc-alignment"),
"kernel-free ifc-geometry must not link ifc-alignment:\\n{tree}"
);
}