main
Drew Owens 0.11.4 source 8889116 1d ago
//! CSWA PHASE 1 (0.11.0) — Drew, 2026-10-15: "pull 3-5 random parts from CSWA
//! exams as part of our e2e testing/validation for each release … We need this
//! bridge to be a real, useable tool, for more than the basics."
//!
//! The new DSL (lines and arcs, revolve, mirror, hole, end conditions, draft,
//! density) through the WHOLE bridge, and the benchmark (swx_selftest
//! {level:"cswa"}). EVERYTHING HERE RUNS IN THE SIMULATOR: the geometry is the
//! mock's arithmetic model against closed forms, NOT a SOLIDWORKS measurement —
//! every new op says `proven.live:false` until a live run measures it.

mod common;

use common::{Bridge, TempDir};
use serde_json::{json, Value};

const PI: f64 = std::f64::consts::PI;

fn s(v: &Value) -> String {
    serde_json::to_string_pretty(v).unwrap_or_default()
}

fn boot(tag: &str) -> (Bridge, TempDir) {
    let machine = TempDir::new(tag);
    std::fs::write(machine.join("windows.json"), "[]").unwrap();
    let b = Bridge::start_with(&[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
    b.call("swx_start", json!({}));
    (b, machine)
}

fn near(a: &Value, b: f64, rel: f64) -> bool {
    a.as_f64().map(|x| (x - b).abs() <= rel * b.abs().max(1e-9)).unwrap_or(false)
}

#[test]
fn a_flanged_bushing_revolved_holed_and_mirrored_weighs_what_the_closed_forms_say() {
    let (b, _m) = boot("cswa-bushing");
    // A flanged bushing, revolved from ONE closed polyline about the sketch's
    // y axis: a Ø40 x 8 flange and a Ø24 x 30 barrel, Ø12 bore.
    let r = b.call("swx_run_script", json!({"script": [
        {"op": "new_part"},
        {"op": "sketch", "plane": "Front Plane", "entities": [
            {"polyline": {"points": [[6, 0], [20, 0], [20, 8], [12, 8], [12, 30], [6, 30]], "close": true}}]},
        {"op": "revolve", "axis": "y"},
        // From the flange's BOTTOM face (the simulator picks faces of a body's
        // box; SOLIDWORKS takes any planar face), 8 mm: through the flange.
        {"op": "hole", "atMm": [16, 0, 0], "diameterMm": 3, "depthMm": 8},
        {"op": "mirror", "plane": "Right Plane"},
        {"op": "set_material", "name": "AISI 1020", "densityKgM3": 7900}
    ]}));
    check!(b, "the script runs end to end", r["success"] == true, s(&r));
    let steps = r["steps"].as_array().cloned().unwrap_or_default();
    let ops: Vec<&str> = steps.iter().map(|x| x["op"].as_str().unwrap_or("")).collect();
    check!(b, "revolve, hole, mirror, set_material ran in order", ops == ["new_part", "sketch", "revolve", "hole", "mirror", "set_material"], format!("{ops:?}"));
    // The 360 revolve and the mirror about a named plane were measured by the first
    // canary (2026-10-16, 0.11.4-rc1, cswa-09 and cswa-10 on SOLIDWORKS 2026) — so on
    // this simulated 2025 they are not live, and nothing is unmeasured.
    for (i, op) in [(2, "revolve"), (4, "mirror")] {
        let p = &steps[i]["result"]["proven"];
        check!(
            b,
            &format!("{op}: measured on 2026 by the canary, so not live on this simulated 2025, and nothing unmeasured"),
            p["live"] == false && p.get("unmeasured").is_none()
                && p["measuredOn"].as_array().map(|a| a.iter().any(|m| m["bridge"] == "0.11.4-rc1" && m["solidworks"] == 2026)).unwrap_or(false),
            s(p)
        );
    }
    // The plain hole and the density were measured by the first live CSWA run
    // (2026-10-15, SOLIDWORKS 2026) — not on the simulated release (2025).
    for (i, op) in [(3, "hole"), (5, "set_material")] {
        let p = &steps[i]["result"]["proven"];
        check!(
            b,
            &format!("{op}: measured on 2026 by the CSWA run, so not live on this simulated 2025, and nothing unmeasured"),
            p["live"] == false && p.get("unmeasured").is_none()
                && p["measuredOn"].as_array().map(|a| a.iter().any(|m| m["bridge"] == "0.11.2" && m["solidworks"] == 2026)).unwrap_or(false),
            s(p)
        );
    }
    check!(b, "the sketch's lines are named as sketch.line", steps[1]["result"]["proven"]["variants"].as_array().map(|a| a.iter().any(|v| v == "sketch.line")).unwrap_or(false), s(&steps[1]["result"]["proven"]));
    let solid = PI * ((400.0 - 36.0) * 8.0 + (144.0 - 36.0) * 22.0);
    let holes = 2.0 * PI * 2.25 * 8.0;
    let ib = b.call("swx_inspect_bodies", json!({}));
    check!(
        b,
        "the volume is the revolved profile less two Ø3 holes through the flange (SIMULATED by the arithmetic model: exact)",
        near(&ib["bodies"][0]["volumeMm3"], solid - holes, 1e-7),
        format!("want {} mm3\n{}", solid - holes, s(&ib))
    );
    let mp = b.call("swx_mass_properties", json!({}));
    check!(
        b,
        "swx_mass_properties integrates the same solid by columns (within 0.1 %): mass = volume x 7900, the material named",
        near(&mp["volumeMm3"], solid - holes, 1e-3) && near(&mp["massG"], (solid - holes) * 7.9e-3, 1e-3) && mp["densityKgM3"] == 7900.0
            && mp["materialName"] == "AISI 1020" && mp["simulatedBy"]["volume"].as_str().unwrap_or("").contains("column"),
        format!("want {} mm3\n{}", solid - holes, s(&mp))
    );
}

#[test]
fn an_open_profile_and_a_crossing_axis_are_refused_before_anything_is_built() {
    let (b, _m) = boot("cswa-refusals");
    let before = b.call("swx_docs", json!({}))["count"].clone();
    let r = b.call("swx_run_script", json!({"script": [
        {"op": "new_part"},
        {"op": "sketch", "entities": [{"polyline": {"points": [[0, 0], [10, 0], [10, 10]]}}]},
        {"op": "extrude", "depthMm": 5}
    ]}));
    check!(b, "an open polyline is refused, naming the gap", r["success"] == false && r["error"].as_str().unwrap_or("").contains("gap of"), s(&r));
    let r = b.call("swx_run_script", json!({"script": [
        {"op": "new_part"},
        {"op": "sketch_rect", "widthMm": 10, "heightMm": 4, "anchor": "center"},
        {"op": "revolve", "axis": "y"}
    ]}));
    check!(b, "a profile across the revolve axis is refused", r["success"] == false && r["error"].as_str().unwrap_or("").contains("crosses the revolve axis"), s(&r));
    check!(b, "...and neither created a document", b.call("swx_docs", json!({}))["count"] == before, "");
}

#[test]
fn the_cswa_level_builds_weighs_and_closes_every_shipped_part() {
    let (b, _m) = boot("cswa-selftest");
    let before = b.call("swx_docs", json!({}))["count"].clone();
    let r = b.call("swx_selftest", json!({"level": "cswa"}));
    let parts = r["cswa"]["parts"].as_array().cloned().unwrap_or_default();
    let n = parts.len();
    check!(
        b,
        "every runnable spec is within tolerance in the simulator, and the summary says so",
        r["success"] == true && r["level"] == "cswa" && n >= 1 && r["cswa"]["allPassed"] == true
            && r["cswa"]["summary"].as_str().unwrap_or("").starts_with(&format!("{n} of {n} CSWA parts within tolerance")),
        s(&r["cswa"])
    );
    check!(b, "the hint LEADS with the verdict", r["_hint"].as_str().unwrap_or("").starts_with(&format!("{n} of {n} CSWA parts")), s(&r["_hint"]));
    let rows = r["results"].as_array().cloned().unwrap_or_default();
    if std::env::var("CSWA_PRINT").is_ok() {
        for x in rows.iter().filter(|x| x["name"].as_str().unwrap_or("").starts_with("CSWA")) {
            eprintln!("{} pass={} {} {}", x["name"], x["pass"], x["checks"], x.get("skipped").cloned().unwrap_or_default());
        }
    }
    let lego = rows.iter().find(|x| x["name"].as_str().unwrap_or("").contains("cswa-00-lego-2x4")).cloned().unwrap_or(Value::Null);
    check!(
        b,
        "spec 0 (the LEGO brick): mass and volume compared against Michael's measured volume, ABS",
        lego["pass"] == true && lego["checks"].as_array().map(|a| a.iter().any(|c| c["quantity"] == "volumeMm3" && c["pass"] == true)).unwrap_or(false),
        s(&lego)
    );
    check!(b, "every part it built was CLOSED", b.call("swx_docs", json!({}))["count"] == before, "");
    let one = b.call("swx_selftest", json!({"level": "cswa", "only": ["cswa-00-lego-2x4"]}));
    check!(b, "only:[id] runs one part", one["cswa"]["ran"] == 1, s(&one["cswa"]));
}

#[test]
fn the_cswa_level_refuses_up_front_with_solidworks_closed() {
    let machine = TempDir::new("cswa-closed");
    std::fs::write(machine.join("windows.json"), "[]").unwrap();
    let b = Bridge::start_with(&[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
    let r = b.call("swx_selftest", json!({"level": "cswa"}));
    check!(b, "refused: SOLIDWORKS is not running, nothing created", r["success"] == false && r["errorCode"] == "solidworks_not_running", s(&r));
}

#[test]
fn the_script_help_teaches_the_cswa_ops() {
    let (b, _m) = boot("cswa-help");
    let r = b.call("swx_run_script", json!({"script": "make me a bracket"}));
    let ops: Vec<String> = r["ops"].as_array().cloned().unwrap_or_default().iter().filter_map(|o| o.as_str().map(String::from)).collect();
    check!(
        b,
        "swx_run_script's help lists revolve, revolve_cut, mirror and hole, with their arguments",
        ["revolve", "revolve_cut", "mirror", "hole"].iter().all(|o| ops.iter().any(|x| x == o) && r["opArgs"].get(*o).is_some()),
        s(&r)
    );
    let ents = r["opArgs"]["sketch"]["entities"].as_str().unwrap_or("");
    check!(
        b,
        "...and every sketch entity kind, and the face-offset plane",
        ["line:", "polyline:", "arc:", "arc3:", "tangentArc:", "slot:", "polygon:", "sketchFillet:", "sketchChamfer:", "construction:true"].iter().all(|k| ents.contains(k))
            && r["opArgs"]["sketch"]["plane"].as_str().unwrap_or("").contains("{offsetFrom: {face:"),
        s(&r["opArgs"]["sketch"])
    );
}

/// THE SPECS DO WHAT THEY SAY (0.11.4). Every spec's `exercises` list is a
/// subset of the variants its steps report — run in the simulator (which
/// classifies each step exactly as the COM host does), or, for a part only
/// SOLIDWORKS can build, classified from its parsed script. Every DERIVED key
/// is reproduced by its `derivation` (tools/cswa-check.py --derivations,
/// where python3 is installed). And a part the simulator cannot build is
/// skipped there, saying why, while the live checker runs it.
#[test]
fn every_spec_exercises_what_it_names_and_every_derived_key_reproduces() {
    let (b, _m) = boot("cswa-exercises");
    let all: Vec<Value> = swx_bridge::cswa::SPECS.iter().map(|(_, t)| serde_json::from_str(t).unwrap()).collect();
    let mut checked = 0;
    for raw in &all {
        let id = raw["id"].as_str().unwrap().to_string();
        let Some(ex) = raw.get("exercises").and_then(|e| e.as_array()) else { continue };
        let spec = swx_bridge::cswa::expand(raw, &all).unwrap();
        let mut seen: Vec<String> = Vec::new();
        if raw.get("simulatorSkips").is_some() {
            let ops = swx_bridge::script::parse(&spec["script"]).unwrap();
            for op in &ops {
                seen.extend(swx_bridge::compat::op_variants(op, &json!({})));
            }
            // A face sketch's side needs the face SOLIDWORKS picked: not claimed here.
            seen.retain(|v| !v.starts_with("sketch.face."));
            for e in ex.iter().filter_map(|e| e.as_str()).filter(|e| !e.starts_with("sketch.face.")) {
                check!(b, &format!("{id} exercises {e} (from its parsed script)"), seen.iter().any(|v| v == e), format!("{seen:?}"));
            }
        } else {
            let r = b.call("swx_run_script", json!({"script": spec["script"]}));
            check!(b, &format!("{id} builds in the simulator"), r["success"] == true, s(&r));
            for st in r["steps"].as_array().cloned().unwrap_or_default() {
                for v in st["result"]["proven"]["variants"].as_array().cloned().unwrap_or_default() {
                    seen.push(v.as_str().unwrap_or("").to_string());
                }
            }
            for e in ex.iter().filter_map(|e| e.as_str()) {
                check!(b, &format!("{id} exercises {e}"), seen.iter().any(|v| v == e), format!("{seen:?}"));
            }
            let _ = b.call("swx_close_doc", json!({"targetDocument": r["documentName"], "save": false}));
        }
        checked += 1;
    }
    check!(b, "the community specs and both Step 4 variants name what they exercise", checked >= 6, format!("{checked}"));
    // Every community spec carries a derivation, and every derivation reproduces its key.
    for raw in &all {
        if raw["id"].as_str().unwrap_or("").contains("community") {
            check!(b, "a community spec derives its key", raw["official"] == false && raw["derivation"].as_array().map(|d| !d.is_empty()).unwrap_or(false), s(raw));
        }
    }
    let out = std::process::Command::new("python3")
        .arg(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tools/cswa-check.py"))
        .arg("--derivations")
        .output();
    if let Ok(o) = out {
        let report: Value = serde_json::from_slice(&o.stdout).unwrap_or(Value::Null);
        check!(
            b,
            "tools/cswa-check.py --derivations reproduces every derived key (massG, volume, centre of mass)",
            o.status.success() && report.as_object().map(|m| m.len() >= 4 && m.values().all(|v| v["ok"] == true)).unwrap_or(false),
            format!("{}\n{}", String::from_utf8_lossy(&o.stdout), String::from_utf8_lossy(&o.stderr))
        );
    }
    // The part only SOLIDWORKS builds: skipped in the simulator, the reason given.
    let r = b.call("swx_selftest", json!({"level": "cswa", "only": ["cswa-08-tool-block-step4", "cswa-08b-tool-block-step4-explicit"]}));
    let rows = r["results"].as_array().cloned().unwrap_or_default();
    let main = rows.iter().find(|x| x["name"].as_str().unwrap_or("").contains("cswa-08-tool-block-step4:")).cloned().unwrap_or(Value::Null);
    check!(
        b,
        "Step 4 by Offset Entities is skipped in the simulator, saying why; the explicit variant runs and lands within the exam's 1 %",
        main["skipped"].as_str().unwrap_or("").starts_with("skipped in the simulator: Offset Entities")
            && r["cswa"]["ran"] == 1 && r["cswa"]["passed"] == 1 && r["cswa"]["skippedInSimulator"] == 1
            && r["cswa"]["summary"] == "1 of 1 CSWA parts within tolerance (1 skipped in the simulator: only SOLIDWORKS can build them)",
        s(&r["cswa"])
    );
}

/// WHICH BUILD RAN (the canary gate): tools/cswa-check.py reads the INSTALLED
/// exe back through ab's read_file in ranges and hashes it, finds the manifest
/// ab installed from (a map or a list in ~/.adom/bridge-install-urls.json), and
/// names its own report (`--report auto`) — checked here against a fake ab, in
/// python3 (skipped where it is not installed).
#[test]
fn the_live_checker_records_which_build_ran() {
    let script = r#"
import base64, hashlib, importlib.util, json, os, sys
spec = importlib.util.spec_from_file_location("cc", sys.argv[1]); cc = importlib.util.module_from_spec(spec); spec.loader.exec_module(cc)
blob = bytes(range(256)) * 9000
files = {"C:\\ab\\solidworks\\swx-bridge.exe": blob, "C:\\ab\\solidworks\\bridge.json": b'{"version":"9.9.9"}',
         "~/.adom/bridge-install-urls.json": json.dumps({"solidworks": "https://wiki.adom.inc/api/v1/pages/solidworks-bridge/files/manifests/canary.json"}).encode()}
def fake(verb, args, timeout=300):
    if verb == "bridge_info":
        return {"success": True, "version": "9.9.9", "installPath": "C:\\ab\\solidworks\\", "manifestUrl": "m", "source": "cache"}
    data = files.get(args.get("path"))
    if data is None:
        return {"success": False, "error": "no such file"}
    if args.get("encoding") == "base64":
        off, n = args.get("offset", 0), args.get("length", 1000000)
        part = data[off:off + n]
        return {"success": True, "content": base64.b64encode(part).decode(), "totalSize": len(data), "nextOffset": off + len(part), "eof": off + len(part) >= len(data)}
    return {"success": True, "content": data.decode()}
cc.call = fake
cc.wiki_json = lambda url: {"sha256": "z" * 64, "version": "9.9.9", "canary": {"rc": "9.9.9-rc1"}} if url.endswith("canary.json") else None
f = cc.bridge_facts()
assert f["exeSha256"] == hashlib.sha256(blob).hexdigest(), f
assert f["bridgeJsonSha256"] == hashlib.sha256(b'{"version":"9.9.9"}').hexdigest(), f
assert f["installedFrom"].endswith("manifests/canary.json") and f["zipSha256"] == "z" * 64 and f["canary"]["rc"] == "9.9.9-rc1", f
files["~/.adom/bridge-install-urls.json"] = json.dumps([{"name": "kicad", "manifestUrl": "k"}, {"name": "solidworks", "manifestUrl": "s"}]).encode()
assert cc.installed_from() == "s"
del files["C:\\ab\\solidworks\\swx-bridge.exe"]
f = cc.bridge_facts()
assert f["exeSha256"] is None and "read_file" in f["exeWhy"], f
p = cc.report_path("auto", {"bridgeVersion": "9.9.9"}, {"versionSupport": {"year": 2026}})
assert os.path.basename(p).startswith("cswa-") and p.endswith("-sw2026-9.9.9.json") and "tools/cswa/reports" in p.replace(os.sep, "/"), p
assert cc.report_path("mine.json", {}, {}) == "mine.json"
assert cc.wiki_json("https://example.com/not-the-wiki.json") is None
print("OK")
"#;
    let out = std::process::Command::new("python3")
        .arg("-c")
        .arg(script)
        .arg(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tools/cswa-check.py"))
        .output();
    let Ok(o) = out else { return };
    assert!(o.status.success() && String::from_utf8_lossy(&o.stdout).contains("OK"), "{}{}", String::from_utf8_lossy(&o.stdout), String::from_utf8_lossy(&o.stderr));
}