fusible 0.11.1

Electronic linter: a fuse that blows to block firmware code generation
use std::path::PathBuf;

use super::settings::{preview_settings, resolve_settings, Settings, Ventilation};
use super::spec::{build_spec, preview_from};
use super::*;

pub(super) fn settings(ventilation: Ventilation) -> Settings {
    resolve_settings(MechanicalConfig::default(), MechanicalConfig::default())
        .map(|s| Settings { ventilation, ..s })
        .unwrap()
}

#[test]
fn cli_overrides_beat_manifest_and_defaults_fill_the_rest() {
    let s = resolve_settings(
        MechanicalConfig {
            format: Some("all".into()),
            cutouts: Some(false),
            ..Default::default()
        },
        MechanicalConfig {
            format: Some("stl".into()),
            wall_thickness: Some(3.0),
            ..Default::default()
        },
    )
    .unwrap();
    assert_eq!(s.formats, FORMATS);
    assert_eq!(s.wall, 3.0);
    assert!(!s.cutouts);
    assert!(s.mounting);
    assert_eq!(s.out_dir, PathBuf::from("dist/mechanical"));
}

#[test]
fn rejects_iges_and_unknown_vents() {
    let bad = |format: &str, vent: &str| {
        resolve_settings(
            MechanicalConfig {
                format: Some(format.into()),
                ventilation: Some(vent.into()),
                ..Default::default()
            },
            MechanicalConfig::default(),
        )
    };
    assert!(matches!(
        bad("iges", "none"),
        Err(MechanicalError::UnknownFormat { .. })
    ));
    assert!(matches!(
        bad("step", "honeycomb"),
        Err(MechanicalError::UnknownVentilation { .. })
    ));
}

#[test]
fn bosses_raise_the_board_and_use_pilot_holes() {
    let model = PcbModel::parse(
        r#"[{"type":"pcb_board","width":80,"height":60,"thickness":1.4},
            {"type":"pcb_hole","x":-36,"y":26,"hole_diameter":3.2}]"#,
    );
    let spec = build_spec(&model, &settings(Ventilation::None), Vec::new()).unwrap();
    assert_eq!(spec.outline.len(), 4);
    assert_eq!(spec.board_z, DEFAULT_WALL + STANDOFF);
    assert_eq!(spec.bosses[0].outer, 7.2);
    assert!((spec.bosses[0].pilot - 2.5).abs() < 1e-9);
    assert!((spec.height - (DEFAULT_WALL + STANDOFF + 1.4 + HEADROOM)).abs() < 1e-9);
}

#[test]
fn height_must_clear_the_board_stack() {
    let model = PcbModel::parse(r#"[{"type":"pcb_board","width":20,"height":20}]"#);
    let s = Settings {
        height: Some(3.0),
        ..settings(Ventilation::None)
    };
    assert!(matches!(
        build_spec(&model, &s, Vec::new()),
        Err(MechanicalError::InvalidDimension { .. })
    ));
    assert!(matches!(
        build_spec(&PcbModel::parse("[]"), &s, Vec::new()),
        Err(MechanicalError::NoBoard)
    ));
}

#[test]
fn preview_forces_glb_and_reports_the_outer_box() {
    let manifest = MechanicalConfig {
        format: Some("step".into()),
        out_dir: Some(PathBuf::from("elsewhere")),
        wall_thickness: Some(3.0),
        ..Default::default()
    };
    let s = preview_settings(manifest).unwrap();
    assert_eq!(s.formats, vec!["glb"]);
    assert_eq!(s.wall, 3.0);
    let model = PcbModel::parse(
        r#"[{"type":"pcb_board","width":40,"height":154,"thickness":1.4},
            {"type":"pcb_hole","x":0,"y":0,"hole_diameter":3.2}]"#,
    );
    let p = preview_from(&build_spec(&model, &s, Vec::new()).unwrap(), vec![1, 2]);
    let height = 3.0 + STANDOFF + 1.4 + HEADROOM;
    assert_eq!(p.glb, vec![1, 2]);
    assert_eq!(p.board_z, 3.0 + STANDOFF);
    assert!((p.height - height).abs() < 1e-9);
    let expected = [40.0 + 7.0, 154.0 + 7.0, height + 3.0];
    for (got, want) in p.size.iter().zip(expected) {
        assert!((got - want).abs() < 1e-9, "{:?}", p.size);
    }
}