fusible 0.8.4

Electronic linter: a fuse that blows to block firmware code generation
use super::diff::{compare_kicad_with_circuit, DiffKind};
use super::sexpr::{extract_footprints, parse_sexpr};
use super::sync::apply_kicad_sync;

#[test]
fn parses_kicad6_footprint_sexpr() {
    let sexpr_str = r#"
        (kicad_pcb (version 20221018) (generator pcbnew)
          (footprint "Resistor_SMD:R_0603_1608Metric"
            (layer "F.Cu")
            (at 125.5 84.2 90)
            (property "Reference" "R1")
            (property "Value" "10k")
          )
          (footprint "Capacitor_SMD:C_0402_1005Metric"
            (layer "B.Cu")
            (at 110.0 95.0)
            (property "Reference" "C1")
          )
        )
    "#;

    let sexpr = parse_sexpr(sexpr_str).expect("S-expression should parse cleanly");
    let footprints = extract_footprints(&sexpr);

    assert_eq!(footprints.len(), 2);
    assert_eq!(footprints[0].designator, "R1");
    assert_eq!(footprints[0].x, 125.5);
    assert_eq!(footprints[0].y, 84.2);
    assert_eq!(footprints[0].rotation, 90.0);
    assert_eq!(footprints[0].layer, "top");

    assert_eq!(footprints[1].designator, "C1");
    assert_eq!(footprints[1].x, 110.0);
    assert_eq!(footprints[1].y, 95.0);
    assert_eq!(footprints[1].rotation, 0.0);
    assert_eq!(footprints[1].layer, "bottom");
}

#[test]
fn compares_and_detects_layout_drift() {
    let sexpr_str = r#"
        (kicad_pcb
          (footprint "R" (layer "F.Cu") (at 10.0 20.0 45) (property "Reference" "R1"))
        )
    "#;
    let sexpr = parse_sexpr(sexpr_str).unwrap();
    let kicad_fps = extract_footprints(&sexpr);

    let circuit_json = r#"[
        { "type": "source_component", "name": "R1", "x": 10.0, "y": -20.0, "rotation": 0.0, "layer": "top" }
    ]"#;

    let report = compare_kicad_with_circuit(&kicad_fps, circuit_json);
    assert_eq!(report.diffs.len(), 1);
    let diff = &report.diffs[0];
    assert_eq!(diff.designator, "R1");
    match diff.kind {
        DiffKind::Rotated { d_rot } => {
            assert!((d_rot - 45.0).abs() < 1e-4);
        }
        _ => panic!("expected rotation diff"),
    }
}

#[test]
fn applies_sync_to_manual_edits() {
    let temp_dir = std::env::temp_dir().join(format!(
        "fusible_test_{}",
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos()
    ));
    let manual_edits_path = temp_dir.join("manual-edits.json");

    let sexpr_str = r#"
        (kicad_pcb
          (footprint "R" (layer "F.Cu") (at 15.0 25.0) (property "Reference" "R1"))
        )
    "#;
    let sexpr = parse_sexpr(sexpr_str).unwrap();
    let kicad_fps = extract_footprints(&sexpr);

    let circuit_json = r#"[
        { "type": "source_component", "name": "R1", "x": 0.0, "y": 0.0, "rotation": 0.0, "layer": "top" }
    ]"#;

    let report = compare_kicad_with_circuit(&kicad_fps, circuit_json);
    let msg = apply_kicad_sync(&report, &manual_edits_path, false).expect("sync should succeed");

    assert!(msg.contains("Synced 1 component placement"));
    assert!(manual_edits_path.is_file());

    let content = std::fs::read_to_string(&manual_edits_path).unwrap();
    assert!(content.contains("\"selector\": \"R1\""));

    let _ = std::fs::remove_dir_all(&temp_dir);
}