main
Drew Owens 0.10.10 source 66cb2da 6d ago
//! HIDEFILEEXT (0.10.10). MEASURED 2026-10-15 on Michael's SOLIDWORKS 2021
//! (COM 29.5.1, Windows 11): swx_selftest {level:"write"} passed every
//! modelling check and failed SIX verbs — swx_design_info, swx_get_parameters,
//! swx_set_parameter, swx_export_step, swx_save, swx_close_doc — all
//! `document_not_open: no open document is titled 'Part14-….SLDPRT'`. With
//! Windows' "Hide extensions for known file types" ON (the Windows default),
//! SOLIDWORKS titles that document `Part14-…`; the self-test addressed it by a
//! title WITH the extension, and the close missed too, leaving it open.
//!
//! `mock-hide-file-ext` makes the simulator title documents the way that
//! machine did. Everything here runs in the simulator.

mod common;

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

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

fn boot(tag: &str, hide: bool) -> (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())]);
    if hide {
        std::fs::write(b.scratch.join("mock-hide-file-ext"), "1").unwrap();
    }
    std::fs::write(b.scratch.join("mock-revision"), "29.5.1").unwrap();
    b.call("swx_start", json!({}));
    (b, machine)
}

#[test]
fn the_selftest_passes_with_extensions_hidden_and_shown_and_leaves_nothing_open() {
    for hide in [true, false] {
        let (b, _m) = boot(if hide { "hfe-selftest-hidden" } else { "hfe-selftest-shown" }, hide);
        let before = b.call("swx_docs", json!({}))["count"].clone();
        let r = b.call("swx_selftest", json!({"level": "write"}));
        let rows = r["results"].as_array().cloned().unwrap_or_default();
        let failed: Vec<&Value> = rows.iter().filter(|x| x["pass"] == false).collect();
        check!(
            b,
            &format!("swx_selftest write, HideFileExt {}: nothing fails (Michael's 6 failures were one bug)", if hide { "ON" } else { "off" }),
            r["success"] == true && failed.is_empty(),
            format!("{}\nFAILED: {failed:?}", s(&r["summary"]))
        );
        for verb in ["swx_design_info", "swx_get_parameters", "swx_set_parameter", "swx_export_step", "swx_save", "swx_close_doc"] {
            check!(
                b,
                &format!("{verb} on the scratch part passes (HideFileExt {hide})"),
                rows.iter().any(|x| x["verb"] == verb && x["pass"] == true),
                s(&Value::Array(rows.clone()))
            );
        }
        check!(
            b,
            "the ownership check is numbered T23, so the rows run T01-T32 without a gap",
            rows.iter().any(|x| x["name"].as_str().unwrap_or("").starts_with("T23 ")),
            ""
        );
        check!(b, "every scratch part was CLOSED", b.call("swx_docs", json!({}))["count"] == before && r.get("documentsLeftOpen").is_none(), s(&r));
        check!(b, "...and struck off the orphan record", b.call("swx_status", json!({}))["orphanedSelftestDocuments"].is_null(), "");
        check!(
            b,
            "the reply says how to call it without being cut off",
            r["_timeoutHint"].as_str().unwrap_or("").starts_with("call this with responseMode:\"stream\" or \"job\""),
            s(&r["_timeoutHint"])
        );
    }
}

#[test]
fn a_title_with_or_without_the_extension_names_the_same_document() {
    let (b, _m) = boot("hfe-titles", true);
    let made = b.call("swx_new_document", json!({"type": "part"}));
    let title = made["created"]["title"].as_str().unwrap_or("").to_string();
    let path = made["documentIdentity"]["path"].as_str().unwrap_or("").to_string();
    check!(
        b,
        "swx_new_document reports the title SOLIDWORKS shows (no extension with HideFileExt ON) AND the path",
        !title.is_empty() && !title.to_ascii_lowercase().ends_with(".sldprt") && path.to_ascii_lowercase().ends_with(".sldprt"),
        s(&made)
    );
    let docs = b.call("swx_docs", json!({}));
    check!(b, "swx_docs lists it under that same title", docs.to_string().contains(&format!("\"{title}\"")), s(&docs));
    let with_ext = format!("{title}.SLDPRT");
    let di = b.call("swx_design_info", json!({"targetDocument": with_ext}));
    check!(b, "swx_design_info finds it by the title WITH the extension", di["success"] != false, s(&di));
    let lower = format!("{title}.sldprt");
    let gp = b.call("swx_get_parameters", json!({"targetDocument": lower}));
    check!(b, "...and with a lower-case extension", gp["success"] != false, s(&gp));
    let act = b.call("swx_activate_document", json!({"name": with_ext}));
    check!(b, "swx_activate_document takes either spelling", act["success"] != false, s(&act));
    let close = b.call("swx_close_doc", json!({"targetDocument": with_ext, "save": false}));
    check!(b, "swx_close_doc closes it by the title with the extension", close["success"] != false, s(&close));
    check!(b, "...and it is gone", b.call("swx_docs", json!({}))["count"] == 0, "");
}

#[test]
fn a_cut_off_selftests_parts_are_reported_by_status_and_closed_by_the_next_run() {
    let (b, _m) = boot("hfe-orphans", true);
    // What a cut-off run leaves: a part it made and recorded, still open.
    let made = b.call("swx_new_document", json!({"type": "part"}));
    let path = made["documentIdentity"]["path"].as_str().unwrap_or("").to_string();
    std::fs::write(b.scratch.join("selftest-docs.json"), json!([{"key": path, "at": "2026-10-15T00:00:00Z"}]).to_string()).unwrap();
    let st = b.call("swx_status", json!({}));
    check!(
        b,
        "swx_status REPORTS the orphan (and closes nothing: a run may be in progress)",
        st["orphanedSelftestDocuments"]["documents"][0]["key"] == path.as_str() && b.call("swx_docs", json!({}))["count"] == 1,
        s(&st["orphanedSelftestDocuments"])
    );
    let r = b.call("swx_selftest", json!({"level": "write", "only": ["T01"]}));
    let rows = r["results"].as_array().cloned().unwrap_or_default();
    check!(
        b,
        "the next swx_selftest closes it first, and says so",
        rows.iter().any(|x| x["name"] == "close what a cut-off self-test left open" && x["pass"] == true),
        s(&Value::Array(rows.clone()))
    );
    check!(b, "nothing is left open, nothing left recorded", b.call("swx_docs", json!({}))["count"] == 0
        && b.call("swx_status", json!({}))["orphanedSelftestDocuments"].is_null(), "");
}