app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
//! ATTACHED IS NOT READY — measured 2026-10-01, and pinned here.
//!
//! On Drew's machine a warm launch attached over COM in 29.7 s; `swx_new_document`
//! 8 s later, `swx_open` at 9 s and `swx_close_all_safely` at 12 s CRASHED
//! SOLIDWORKS while its 3DEXPERIENCE add-in was still initialising (its
//! performance log: LOAD_ADDIN lines still running, then SWEXH / UNHANDLED, the
//! 3DEXPERIENCE marker only AFTER the document commands, SWERR_REPORT). A control
//! launch with no document commands crashed nothing and logged `<3DXLOAD_TIME>`
//! when the platform load finished. So:
//!
//! * every document-creating, -opening, -saving or -closing verb, and the quit,
//! refuses `host_app_starting` until SOLIDWORKS has finished starting;
//! * `swx_start` reports `stage:"ready"` only then, and `swx_status` exposes
//! `solidworks.ready` with what it is based on;
//! * two independent signals: COM's `StartupProcessCompleted`, and the
//! performance log (a 3DEXPERIENCE marker after the last LOAD_ADDIN line);
//! * a sweep during which SOLIDWORKS dies says `host_app_gone`, with the log's
//! crash markers, never `dialog_census_unknown`;
//! * SOLIDWORKS' `~$` lock files are not offered as rescues.
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 a_visible_machine(tag: &str) -> TempDir {
let d = TempDir::new(tag);
std::fs::write(d.join("windows.json"), "[]").unwrap();
d
}
fn session(scratch: &TempDir, docs: Value) {
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({"running": true, "untitled": 1, "active": Value::Null, "docs": docs, "files": {}}).to_string(),
)
.unwrap();
}
/// Minimal arguments for each gated verb — enough to reach the gate, which comes
/// before every one of them.
fn gated_calls() -> Vec<(&'static str, Value)> {
let reason = "the user asked for it";
vec![
("swx_new_document", json!({"type": "part"})),
("swx_open", json!({"path": "C:\\Users\\drew\\work\\bracket.SLDPRT"})),
("swx_import", json!({"path": "C:\\Users\\drew\\work\\x.step"})),
("swx_save", json!({})),
("swx_save_as", json!({"outputPath": "C:\\out\\x.SLDPRT"})),
("swx_run_script", json!({"script": []})),
("swx_run_macro", json!({"path": "C:\\x.swp", "reason": reason})),
("swx_add_component", json!({"path": "C:\\x.SLDPRT"})),
("swx_close_doc", json!({"name": "Part1"})),
("swx_close_all_documents", json!({"confirm": true})),
("swx_close_all_safely", json!({"reason": reason})),
("swx_close_import_temps", json!({"reason": reason})),
("swx_reset_workspace", json!({})),
("swx_shutdown", json!({"reason": reason})),
("swx_stop", json!({"userAuthorized": true, "reason": reason})),
]
}
#[test]
fn document_verbs_wait_for_solidworks_to_finish_starting() {
let scratch = TempDir::new("ready-gate");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
let machine = a_visible_machine("ready-gate-machine");
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
let started = b.call("swx_start", json!({}));
check!(
b,
"swx_start: attached but still starting — stage `starting`, never `ready`",
started["success"] == true
&& started["stage"] == "starting"
&& started["ready"] == false
&& started["stillStarting"] == true,
s(&started)
);
let st = b.call("swx_status", json!({}));
check!(
b,
"swx_status: `solidworks.ready` is false, and says what it is based on",
st["solidworks"]["ready"] == false
&& st["solidworks"]["readiness"]["basedOn"]
.as_array()
.map(|a| a.iter().any(|x| x == "StartupProcessCompleted"))
.unwrap_or(false),
s(&st["solidworks"])
);
for (verb, args) in gated_calls() {
let r = b.call(verb, args);
check!(
b,
&format!("{verb}: refuses host_app_starting with retryAfterSeconds until SOLIDWORKS is ready"),
r["success"] == false && r["errorCode"] == "host_app_starting" && r["retryAfterSeconds"] == 5,
s(&r)
);
}
// A READ verb is gated too while the start is VERIFIED incomplete (0.9.5:
// every verb not on STARTUP_EXEMPT waits).
let early = b.call("swx_docs", json!({}));
check!(b, "swx_docs: a COM read waits too while SOLIDWORKS is verifiably starting", early["errorCode"] == "host_app_starting", s(&early));
// SOLIDWORKS FINISHES STARTING.
std::fs::remove_file(scratch.join("mock-startup-incomplete")).unwrap();
let again = b.call("swx_start", json!({}));
check!(b, "swx_start: now `ready`", again["stage"] == "ready" && again["ready"] == true, s(&again));
let docs = b.call("swx_docs", json!({}));
check!(b, "swx_docs: ...and nothing was sent while it was starting: no document appeared", docs["count"] == 0, s(&docs));
let made = b.call("swx_new_document", json!({"type": "part"}));
check!(b, "swx_new_document: runs once SOLIDWORKS is ready", made["success"] == true, s(&made));
}
/// THE PERFORMANCE LOG IS A SECOND, INDEPENDENT WITNESS. COM saying "complete"
/// is not enough while the log shows the latest start's add-ins still loading;
/// the 3DEXPERIENCE marker after them is what completes it. A log that has never
/// held such a marker cannot say, and is not used.
#[test]
fn the_performance_log_is_a_second_independent_witness() {
let scratch = TempDir::new("ready-perflog");
session(&scratch, json!([]));
let log = scratch.join("SolidWorksPerformance.log");
// An earlier session finished; this one is loading add-ins.
std::fs::write(
&log,
"<LOAD_ADDIN> old\n<3DXLOAD_TIME>23000</3DXLOAD_TIME>\n<LOAD_ADDIN> SolidWorks Toolbox\n<LOAD_ADDIN> 3DEXPERIENCE\n",
)
.unwrap();
let logp = log.to_string_lossy().to_string();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"swx_new_document: refused while the log shows add-ins still loading, though COM says complete",
r["errorCode"] == "host_app_starting"
&& r["readiness"]["startupProcessCompleted"] == true
&& r["readiness"]["performanceLog"]["verdict"] == "loading"
&& r["readiness"]["basedOn"].as_array().map(|a| a.len() == 2).unwrap_or(false),
s(&r)
);
std::fs::write(
&log,
format!("{}<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n", std::fs::read_to_string(&log).unwrap()),
)
.unwrap();
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(b, "swx_new_document: runs once the 3DEXPERIENCE marker follows the add-ins", r["success"] == true, s(&r));
// A SESSION THAT NEVER LOADED THE PLATFORM ADD-IN (0.9.7, F2): no marker
// is coming. Its log has gone quiet after its add-ins, and COM says the
// start completed: ready, and it says why.
let scratch2 = TempDir::new("ready-perflog-none");
session(&scratch2, json!([]));
let log2 = scratch2.join("SolidWorksPerformance.log");
std::fs::write(&log2, "<LOAD_ADDIN> SolidWorks Toolbox\n").unwrap();
std::fs::File::options()
.write(true)
.open(&log2)
.unwrap()
.set_modified(std::time::SystemTime::now() - std::time::Duration::from_secs(300))
.unwrap();
let logp2 = log2.to_string_lossy().to_string();
let b2 = Bridge::start_in(TempDir::borrowed(&scratch2.0), &[("SWX_PERF_LOG", &logp2)]);
// The FIRST assessment says what it was based on; later ones say the session
// was already found ready.
let st = b2.call("swx_start", json!({}));
check!(
b2,
"swx_start: platform add-in never loaded, log quiet, COM true => ready, basis platformAddInNotLoaded",
st["ready"] == true
&& st["readiness"]["performanceLog"]["verdict"] == "platformAddInNotLoaded"
&& st["readiness"]["basedOn"] == json!(["StartupProcessCompleted", "performanceLog"]),
s(&st)
);
let st2 = b2.call("swx_status", json!({}));
check!(
b2,
"swx_status: ...and a session found ready stays ready",
st2["solidworks"]["ready"] == true
&& st2["solidworks"]["readiness"]["basedOn"] == json!(["alreadyReadyThisSession"]),
s(&st2["solidworks"])
);
}
/// WITH NO SIGNAL AT ALL, a grace after first sight stands in — and says so. It
/// is not "ready" before the grace has passed.
#[test]
fn with_no_readiness_signal_the_grace_stands_in_and_says_so() {
let scratch = TempDir::new("ready-grace");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"swx_new_document: no signal and a session just seen — still starting, based on the grace",
r["errorCode"] == "host_app_starting"
&& r["readiness"]["basedOn"] == json!(["graceAfterFirstSeen"])
&& r["readiness"]["startupProcessCompleted"].is_null(),
s(&r)
);
}
/// SOLIDWORKS DIES MID-SWEEP: `host_app_gone`, with what was preserved (verified)
/// and closed, and the crash markers from its performance log — never
/// `dialog_census_unknown`.
#[test]
fn a_sweep_during_which_solidworks_dies_says_host_app_gone() {
let scratch = TempDir::new("ready-gone");
session(
&scratch,
json!([
{"docType": "part", "path": "untitled:a", "title": "Part1", "saved": false, "dirty": true},
{"docType": "part", "path": "untitled:b", "title": "Part2", "saved": false, "dirty": true}
]),
);
let log = scratch.join("SolidWorksPerformance.log");
std::fs::write(
&log,
"<SWEXH>an older session's crash</SWEXH>\n<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
<DOC_OPEN> \".prtdot\"\n<SWEXH>\n<UNHANDLED>\n</SWEXH>\n<RUNTIME>18</RUNTIME>\n<SWERR_REPORT>\n",
)
.unwrap();
let logp = log.to_string_lossy().to_string();
let machine = a_visible_machine("ready-gone-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path()), ("SWX_PERF_LOG", &logp)],
);
b.call("swx_start", json!({}));
std::fs::write(scratch.join("mock-crash-after-first-save"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
let evidence: Vec<String> = r["crashEvidence"]
.as_array()
.map(|a| a.iter().filter_map(|x| x.as_str().map(|s| s.to_string())).collect())
.unwrap_or_default();
check!(
b,
"swx_close_all_safely: SOLIDWORKS died mid-sweep — host_app_gone, not dialog_census_unknown",
r["success"] == false && r["errorCode"] == "host_app_gone" && !s(&r).contains("dialogCensusUnknown"),
s(&r)
);
check!(
b,
"swx_close_all_safely: ...with what was preserved before it went, and what was closed",
r["preserved"] == 1 && r["closed"] == 0,
s(&r)
);
check!(
b,
"swx_close_all_safely: ...and this session's crash markers from the performance log, not an older session's",
evidence.iter().any(|l| l.contains("SWERR_REPORT"))
&& evidence.iter().any(|l| l.contains("UNHANDLED"))
&& !evidence.iter().any(|l| l.contains("older session")),
s(&r["crashEvidence"])
);
}
/// SOLIDWORKS' `~$` LOCK FILE is not a rescue, and not a failed one.
#[test]
fn a_lock_file_in_a_recovery_folder_is_named_apart() {
let scratch = TempDir::new("ready-lock");
let dir = scratch.join("recovery").join("20261001-104300");
std::fs::create_dir_all(&dir).unwrap();
let mut body = vec![0x9f, 0xe4, 0x18, 0x9f, 0, 0, 0, 4];
body.resize(26_129, b'x');
std::fs::write(dir.join("Part1.SLDPRT"), &body).unwrap();
std::fs::write(dir.join("~$Part1.SLDPRT"), b"lock").unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
let l = b.call("swx_recover_list", json!({}));
let sweep = l["sweeps"][0].clone();
check!(
b,
"swx_recover_list: the lock file is listed apart, as a lock file — never as a file to open",
!sweep["files"].as_array().map(|a| a.iter().any(|f| f["name"] == "~$Part1.SLDPRT")).unwrap_or(true)
&& sweep["lockFiles"][0]["name"] == "~$Part1.SLDPRT"
&& sweep["lockFiles"][0]["lockFile"] == true
&& sweep["lockFiles"][0]["lockOf"] == "Part1.SLDPRT",
s(&sweep)
);
}
/// A NOTIFICATION DOES NOT STOP A SWEEP (measured 2026-10-01): the 3DEXPERIENCE
/// "Connected to …" notice — owner frame enabled, a tool window, nothing to click
/// — appeared ~18 s after a document create and stayed for minutes. Under 0.9.3
/// it blocked every quit, sweep and import in that window. Now the sweep runs and
/// the notice is reported with its evidence; it is never answered.
#[test]
fn a_notification_does_not_stop_a_sweep_and_is_reported() {
let scratch = TempDir::new("notice-sweep");
session(
&scratch,
json!([{"docType": "part", "path": "untitled:a", "title": "Part1", "saved": false, "dirty": true}]),
);
let machine = TempDir::new("notice-sweep-machine");
std::fs::write(
machine.join("windows.json"),
json!([{"hwnd": 331370, "className": "#32770", "title": "", "visible": true,
"owner": 4242, "ownerEnabled": true, "style": 0x9400_0044u64,
"exStyle": 0x90080u64, "rect": [533, 45, 834, 112]}])
.to_string(),
)
.unwrap();
std::fs::write(
machine.join("dialog-open.json"),
json!({"hwnd": 331370, "controls": [["Static", 0, "Title", true], ["Static", 0, "Description", true],
["Static", 0, "Button1", false], ["Static", 0, "Button2", false]]})
.to_string(),
)
.unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
b.call("swx_start", json!({}));
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
check!(
b,
"swx_close_all_safely: the notification does not stop the sweep",
r["success"] == true && r["done"] == true && r["stoppedByDialog"].is_null(),
s(&r)
);
check!(
b,
"swx_close_all_safely: ...and is reported, with its evidence, never answered",
r["notifications"][0]["kind"] == "notification"
&& r["notifications"][0]["neverAnswer"] == true
&& r["notifications"][0]["evidence"]["window"]["toolWindow"] == true,
s(&r["notifications"])
);
}
/// THE SESSION'S DOCUMENT HISTORY IS IN swx_status (0.9.5), so a caller can
/// predict whether a quit will need priming: null with its basis for a session
/// the bridge did not watch from its start, true once a document has been open.
#[test]
fn swx_status_reports_the_sessions_document_history() {
let scratch = TempDir::new("history-status");
session(&scratch, json!([]));
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({}));
let st = b.call("swx_status", json!({}));
let h = st["solidworks"]["sessionHistory"].clone();
check!(
b,
"swx_status: a session the bridge attached to has an UNKNOWN history, and says why",
h.get("everHadDocument").map(|v| v.is_null()).unwrap_or(false)
&& h["basis"].as_str().unwrap_or("").contains("did not watch"),
s(&st["solidworks"])
);
let made = b.call("swx_new_document", json!({"type": "part"}));
assert!(made["success"] == true, "precondition: a document was made: {}", s(&made));
let st = b.call("swx_status", json!({}));
check!(
b,
"swx_status: ...and once a document has been open in it, everHadDocument is true",
st["solidworks"]["sessionHistory"]["everHadDocument"] == true,
s(&st["solidworks"]["sessionHistory"])
);
}
// ── 0.9.5: GATED BY DEFAULT, THE LOG BOUND TO THE SESSION (adversary pass 42) ─
/// The exemption list, name by name. A verb not here waits for SOLIDWORKS to
/// finish starting — a NEW verb included — so adding one here is a decision
/// this test makes visible. tests/wiring.rs pins the same list against the
/// source.
const EXEMPT: &[&str] = &[
"swx_status", "swx_readiness", "swx_start", "swx_describe", "swx_progress",
"swx_action_ledger", "swx_verb_times", "swx_uninstall", "swx_find_files",
"swx_recover_list", "swx_window_info", "swx_dismiss_blocking_dialogs",
"swx_close_window", "swx_screenshot", "swx_set_driving_badge", "swx_kill", "swx_user_presence", "swx_sign_in",
"swx_selftest", "swx_com_respawn",
];
#[test]
fn every_verb_not_exempt_waits_for_startup_by_default() {
let scratch = TempDir::new("ready-default");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
let machine = a_visible_machine("ready-default-machine");
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
b.call("swx_start", json!({}));
let verbs: Vec<String> = b.call("swx_describe", json!({}))["verbs"]
.as_array()
.unwrap()
.iter()
.map(|v| v["verb"].as_str().unwrap().to_string())
.collect();
check!(b, "the catalog is the whole surface", verbs.len() > 60, format!("{verbs:?}"));
let mut through = Vec::new();
for v in verbs.iter().filter(|v| !EXEMPT.contains(&v.as_str())) {
let r = b.call(v, json!({}));
if r["errorCode"] != "host_app_starting" {
through.push(format!("{v}: {}", s(&r)));
}
}
check!(b, "every verb not on the exemption list is held at the gate while SOLIDWORKS starts", through.is_empty(), through.join("\n"));
let mut held = Vec::new();
for v in EXEMPT.iter().filter(|v| !matches!(**v, "swx_uninstall" | "swx_start")) {
let r = b.call(v, json!({}));
if r["errorCode"] == "host_app_starting" {
held.push(v.to_string());
}
}
check!(b, "...and no exempt verb is", held.is_empty(), format!("{held:?}"));
}
/// THE GRACE UNLOCKS READS, NEVER DOCUMENT COMMANDS. With no signal that can
/// verify the start, a read verb runs once the grace is over; a verb that
/// creates a document still refuses — "cannot verify startup completed".
#[test]
fn the_grace_lets_reads_run_and_never_a_document_command() {
let scratch = TempDir::new("ready-grace-reads");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_READINESS_GRACE_SECS", "0")]);
b.call("swx_start", json!({}));
let docs = b.call("swx_docs", json!({}));
check!(b, "swx_docs: a READ runs once the grace is over", docs["success"] != false && docs["count"] == 0, s(&docs));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"swx_new_document: still refused — the clock never authorises a document command",
r["errorCode"] == "host_app_starting"
&& r["startingUnverifiable"] == true
&& r["_hint"].as_str().unwrap_or("").contains("Cannot verify startup completed")
&& r["readiness"]["state"] == "unverifiable"
&& r["readiness"]["readsAllowed"] == true,
s(&r)
);
let st = b.call("swx_status", json!({}));
check!(b, "swx_status: `ready` is false on an unverifiable session", st["solidworks"]["ready"] == false, s(&st["solidworks"]));
// Before the grace is over, even a read waits.
let scratch2 = TempDir::new("ready-grace-early");
session(&scratch2, json!([]));
std::fs::write(scratch2.join("mock-startup-unreadable"), "").unwrap();
let b2 = Bridge::start_in(TempDir::borrowed(&scratch2.0), &[]);
b2.call("swx_start", json!({}));
let early = b2.call("swx_docs", json!({}));
check!(b2, "swx_docs: inside the grace a read waits too", early["errorCode"] == "host_app_starting", s(&early));
}
fn write_log(scratch: &TempDir, text: &str, age_secs: u64) -> String {
let log = scratch.join("SolidWorksPerformance.log");
std::fs::write(&log, text).unwrap();
std::fs::File::options()
.write(true)
.open(&log)
.unwrap()
.set_modified(std::time::SystemTime::now() - std::time::Duration::from_secs(age_secs))
.unwrap();
log.to_string_lossy().to_string()
}
/// COM ALONE IS NEVER ENOUGH WHILE THE MACHINE WRITES MARKERS. Measured
/// 2026-10-01: StartupProcessCompleted was TRUE before the marker. A log whose
/// marker predates this process cannot vouch for it — and does not hand the
/// verdict to COM either.
#[test]
fn com_alone_never_vouches_for_a_session_with_no_marker_of_its_own() {
// The log's last write predates the (hour-old) process: an earlier session.
let scratch = TempDir::new("ready-com-alone");
session(&scratch, json!([]));
let logp = write_log(&scratch, "<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n", 7200);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"swx_new_document: COM true + a log from before this process => refused, unverifiable",
r["errorCode"] == "host_app_starting"
&& r["readiness"]["startupProcessCompleted"] == true
&& r["readiness"]["performanceLog"]["verdict"] == "notThisSession"
&& r["startingUnverifiable"] == true,
s(&r)
);
// This session is writing the log right now, with add-ins loading and no
// marker yet: still loading (0.9.7: no marker history needed).
let scratch2 = TempDir::new("ready-no-marker-yet");
session(&scratch2, json!([]));
let logp2 = write_log(&scratch2, "<LOAD_ADDIN> SolidWorks Toolbox\n<LOAD_ADDIN> 3DEXPERIENCE\n", 0);
let b2 = Bridge::start_in(TempDir::borrowed(&scratch2.0), &[("SWX_PERF_LOG", &logp2)]);
b2.call("swx_start", json!({}));
let r2 = b2.call("swx_new_document", json!({"type": "part"}));
check!(
b2,
"swx_new_document: a log being written with add-ins loading and no marker yet => loading, refused",
r2["errorCode"] == "host_app_starting" && r2["readiness"]["performanceLog"]["verdict"] == "loading",
s(&r2)
);
}
/// AN ON-DEMAND ADD-IN IS TOLD FROM A START BY QUIET AND BY WHICH ADD-IN. A
/// LOAD_ADDIN after the marker in a log written moments ago is a start still
/// loading; one that names the 3DEXPERIENCE add-in is a START (wedged, however
/// quiet), never an on-demand load.
#[test]
fn an_on_demand_add_in_needs_quiet_and_is_never_the_3dexperience_add_in() {
for (tag, text, age, why) in [
(
"ready-ondemand-fresh",
"<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SOLIDWORKS Simulation\n",
0u64,
"a LOAD_ADDIN written moments ago",
),
(
"ready-ondemand-3dx",
"<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SolidWorks Toolbox\n\
<LOAD_ADDIN> \"SWK3DCompassAddin.dll\" \"3DEXPERIENCE for WORKS\"\n",
300,
"a quiet log whose LOAD_ADDIN after the marker is the platform add-in (SWK3DCompassAddin.dll)",
),
(
"ready-header-scope",
"<LOAD_ADDIN> old\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n; SolidWorks generated log file - do not edit.\n\
<VERSION> 6</VERSION>\n<PROC_NUM>8</PROC_NUM>\n",
0,
"a marker from BEFORE the start's header (the file is rewritten at every start)",
),
] {
let scratch = TempDir::new(tag);
session(&scratch, json!([]));
// A START is a young process (0.9.11: past ten minutes, a session with
// COM true and quiet startup lines is long-running, never starting).
created_ago(&scratch, 400);
let logp = write_log(&scratch, text, age);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
&format!("swx_new_document: {why} is a start still loading, refused"),
r["errorCode"] == "host_app_starting" && r["readiness"]["performanceLog"]["verdict"] == "loading",
s(&r)
);
}
// An on-demand-looking log with COM UNABLE to answer: the on-demand rule
// needs COM's "completed" as well, so this stays unverifiable.
let scratch = TempDir::new("ready-ondemand-nocom");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
let logp = write_log(
&scratch,
"<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SOLIDWORKS Simulation\n",
120,
);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"swx_new_document: an on-demand-looking log without COM's word is not a verified start",
r["errorCode"] == "host_app_starting" && r["startingUnverifiable"] == true,
s(&r)
);
// An add-in whose NAME carries "3DEXPERIENCE" but is not the platform —
// measured 2026-10-01: SaveFolderTo3DEXPERIENCEAddin2.DLL — loaded after the
// marker in a quiet log is on-demand, not a start.
let scratch = TempDir::new("ready-ondemand-savefolder");
session(&scratch, json!([]));
let logp = write_log(
&scratch,
"<LOAD_ADDIN> \"SWK3DCompassAddin.dll\" \"3DEXPERIENCE for WORKS\"\n<3DXINITERROR>0\n\
<LOAD_ADDIN> \"SaveFolderTo3DEXPERIENCEAddin2.DLL\" \"Batch Save to 3DEXPERIENCE\"\n",
120,
);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let st = b.call("swx_start", json!({}));
check!(
b,
"swx_start: a non-platform add-in naming 3DEXPERIENCE, after the marker, quiet => on demand, ready",
st["ready"] == true && st["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete",
s(&st)
);
// ...and the on-demand case itself, with the rule's basis in the answer.
let scratch = TempDir::new("ready-ondemand-ok");
session(&scratch, json!([]));
let logp = write_log(
&scratch,
"<LOAD_ADDIN> 3DEXPERIENCE\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> SOLIDWORKS Simulation\n",
120,
);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let st = b.call("swx_start", json!({}));
check!(
b,
"swx_start: COM true + marker + a quiet non-3DEXPERIENCE LOAD_ADDIN => ready, saying why",
st["ready"] == true
&& st["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete"
&& st["readiness"]["performanceLog"]["why"].as_str().unwrap_or("").contains("on demand"),
s(&st)
);
}
/// A DOCUMENT THAT PERSISTS ACROSS TWO POLLS IS RECORDED (0.9.6). The user's
/// own document, open when the bridge first looks, never passed through a verb
/// that "made one": the readiness polls record it — once the count has been
/// > 0 on two distinct polls of the STARTED session, never from one read.
#[test]
fn a_document_that_persists_across_two_polls_marks_the_session() {
let scratch = TempDir::new("ready-poll-marks");
session(
&scratch,
json!([{"docType": "part", "path": "C:\\Users\\drew\\work\\bracket.SLDPRT",
"title": "bracket.SLDPRT", "saved": true, "dirty": false}]),
);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({}));
let first = b.call("swx_status", json!({}));
let h1 = first["solidworks"]["sessionHistory"].clone();
check!(
b,
"swx_status: what the poll saw is recorded, with its count",
h1["observations"].as_array().map(|a| a.iter().any(|o| o["count"] == 1)).unwrap_or(false),
s(&h1)
);
std::thread::sleep(std::time::Duration::from_millis(1100));
let st = b.call("swx_status", json!({}));
check!(
b,
"swx_status: the count persisted across two polls — the session has held a document",
st["solidworks"]["sessionHistory"]["everHadDocument"] == true
&& st["solidworks"]["sessionHistory"]["advisory"] == true,
s(&st["solidworks"])
);
}
/// EVERY GATED VERB SAYS WHAT THE GATE SAW (0.9.6, G3) — on success as well as
/// on refusal — so a live run can prove no COM verb ran while starting. Including
/// the arms that return straight from the handler table (swx_show_*, exports).
#[test]
fn every_gated_reply_carries_the_readiness_the_gate_evaluated() {
let scratch = TempDir::new("ready-at-gate");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({}));
let refused = b.call("swx_docs", json!({}));
check!(
b,
"refusal: readiness.ready false, with basedOn",
refused["errorCode"] == "host_app_starting"
&& refused["readiness"]["ready"] == false
&& refused["readiness"]["basedOn"].as_array().map(|a| !a.is_empty()).unwrap_or(false),
s(&refused)
);
std::fs::remove_file(scratch.join("mock-startup-incomplete")).unwrap();
for (verb, args) in [
("swx_docs", json!({})),
("swx_new_document", json!({"type": "part"})),
("swx_show_part", json!({"path": "C:\\Users\\drew\\work\\nothing-here.SLDPRT"})),
("swx_get_app_state", json!({})),
] {
let r = b.call(verb, args);
check!(
b,
&format!("{verb}: the reply carries readiness as evaluated at the gate"),
r["readiness"]["evaluatedAt"] == "gate"
&& r["readiness"]["ready"] == true
&& r["readiness"]["basedOn"].as_array().map(|a| !a.is_empty()).unwrap_or(false),
s(&r)
);
}
let free = b.call("swx_find_files", json!({"root": "/nonexistent"}));
check!(b, "an exempt verb is not gated and says nothing of a gate", free["readiness"].is_null(), s(&free));
}
/// THE TWO-POLL RULE, both edges (0.9.6). Two counts > 0 read within a second
/// are one blip, not persistence; and a zero between two counts breaks the run.
#[test]
fn a_blip_or_an_interrupted_count_never_records_a_document() {
let scratch = TempDir::new("ready-blip");
session(&scratch, json!([]));
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({}));
// A blip: a count of 1 on two reads close together, then 0.
std::fs::write(scratch.join("mock-phantom-doc-once"), "2").unwrap();
b.call("swx_docs", json!({}));
b.call("swx_docs", json!({}));
std::thread::sleep(std::time::Duration::from_millis(1100));
let st = b.call("swx_status", json!({}));
check!(
b,
"two counts > 0 within a second, then zero: not a document",
st["solidworks"]["sessionHistory"]["everHadDocument"] != true,
s(&st["solidworks"]["sessionHistory"])
);
// Interrupted: 1 ... 0 ... (a second later) 1.
std::fs::write(scratch.join("mock-phantom-doc-once"), "1").unwrap();
std::fs::remove_file(scratch.join("mock-phantom-doc-once.used")).unwrap();
b.call("swx_docs", json!({}));
b.call("swx_docs", json!({}));
std::thread::sleep(std::time::Duration::from_millis(1100));
std::fs::remove_file(scratch.join("mock-phantom-doc-once.used")).unwrap();
let st = b.call("swx_status", json!({}));
check!(
b,
"a count, a zero, a count a second later: the zero breaks the run — not a document",
st["solidworks"]["sessionHistory"]["everHadDocument"] != true,
s(&st["solidworks"]["sessionHistory"])
);
}
/// THE PLATFORM ADD-IN LOADED AND ITS MARKER NEVER CAME: held, however quiet
/// the log (a dialog, a sign-in) — and the log's SHAPE is recorded: a file that
/// begins with the start header is rewritten per start; two headers is an
/// appended log, of which only the last section is read (0.9.7, F2/F7).
#[test]
fn a_platform_load_without_its_marker_is_held_and_the_logs_shape_is_recorded() {
const COMPASS: &str = r#"<AD2> "LOAD_ADDIN" "SWK3DCompassAddin.dll" "3DEXPERIENCE for WORKS" [2.18] "6.34.0.2027" 14 COM.new 1770885081 13"#;
let scratch = TempDir::new("ready-platform-no-marker");
session(&scratch, json!([]));
created_ago(&scratch, 400); // a start: a young process (0.9.11), its log written since
let logp = write_log(
&scratch,
&format!("; SolidWorks generated log file - do not edit.\n<VERSION> 6</VERSION>\n{COMPASS}\n"),
300,
);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"swx_new_document: the platform add-in loaded, no marker, quiet => loading, refused",
r["errorCode"] == "host_app_starting"
&& r["readiness"]["performanceLog"]["verdict"] == "loading"
&& r["readiness"]["performanceLog"]["shape"]["model"] == "rewrittenPerStart"
&& r["readiness"]["performanceLog"]["lengthAtFirstSight"].is_u64(),
s(&r)
);
// No platform add-in, a quiet log — but COM cannot answer: that verdict
// needs COM's "completed" too, so the session is unverifiable.
let scratch = TempDir::new("ready-no-platform-no-com");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-unreadable"), "").unwrap();
let logp = write_log(&scratch, "<LOAD_ADDIN> \"SWToolbox.dll\"\n", 300);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"platformAddInNotLoaded without COM's word is not a verified start",
r["errorCode"] == "host_app_starting"
&& r["startingUnverifiable"] == true
&& r["readiness"]["performanceLog"]["verdict"] == "platformAddInNotLoaded",
s(&r)
);
let scratch = TempDir::new("ready-appended");
session(&scratch, json!([]));
let logp = write_log(
&scratch,
"; SolidWorks generated log file - do not edit.\n<LOAD_ADDIN> old\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
; SolidWorks generated log file - do not edit.\n<VERSION> 6</VERSION>\n<LOAD_ADDIN> \"SWToolbox.dll\"\n",
0,
);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"an APPENDED log: the earlier start's marker is not read; this start is loading",
r["errorCode"] == "host_app_starting"
&& r["readiness"]["performanceLog"]["verdict"] == "loading"
&& r["readiness"]["performanceLog"]["shape"]["model"] == "appended",
s(&r)
);
}
/// MODALITY IS A PERSISTENT MEASUREMENT, NOT ONE READ (0.9.8). MEASURED
/// 2026-10-01 on 0.9.7: the 3DEXPERIENCE Connected notice — hwnd 4329196,
/// `#32770`, style 0x94000044, exstyle 0x90080, 301x67, nine Static controls
/// (3 visible, 6 hidden) reading the template placeholders — was read with its
/// owner frame DISABLED while SOLIDWORKS was busy inside the sweep's own close,
/// and stopped `swx_close_all_safely` as a modal; seconds later the frame was
/// enabled again. The census now re-reads the owner when it is the ONLY fact
/// that blocks: disabled then enabled -> a notification, reported with the read
/// history; disabled on every read -> still a modal. And a window with a
/// Button-class control is never settled at all.
fn sweep_with_notice(tag: &str, owner_reads: Value, extra_controls: Value) -> (Bridge, Value, TempDir, TempDir) {
let scratch = TempDir::new(tag);
session(
&scratch,
json!([{"docType": "part", "path": "untitled:a", "title": "Part1", "saved": false, "dirty": true}]),
);
let machine = TempDir::new(&format!("{tag}-machine"));
std::fs::write(
machine.join("windows.json"),
json!([{"hwnd": 4329196, "className": "#32770", "title": "", "visible": true,
"owner": 856844, "ownerEnabled": false, "ownerEnabledReads": owner_reads,
"style": 0x9400_0044u64, "exStyle": 0x90080u64, "rect": [533, 123, 834, 190]}])
.to_string(),
)
.unwrap();
let mut controls = vec![
json!(["Static", 0, "Title", true]), json!(["Static", 0, "Description", true]), json!(["Static", 0, "", true]),
json!(["Static", 0, "Button1", false]), json!(["Static", 0, "Button2", false]),
json!(["Static", 0, "Hyperlink Text 1", false]), json!(["Static", 0, "Hyperlink Text 2", false]),
json!(["Static", 0, "", false]), json!(["Static", 0, "", false]),
];
controls.extend(extra_controls.as_array().cloned().unwrap_or_default());
std::fs::write(
machine.join("dialog-open.json"),
json!({"hwnd": 4329196, "controls": controls}).to_string(),
)
.unwrap();
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path()), ("SWX_OWNER_SETTLE_MS", "30")],
);
b.call("swx_start", json!({}));
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
(b, r, scratch, machine)
}
#[test]
fn an_owner_disabled_for_an_instant_does_not_make_a_notice_a_modal() {
let (b, r, _s, _m) = sweep_with_notice("settle-transient", json!([false, false, true]), json!([]));
check!(
b,
"swx_close_all_safely: the notice whose owner was busy for an instant does not stop the sweep",
r["success"] == true && r["done"] == true && r["stoppedByDialog"].is_null(),
s(&r)
);
let n = &r["notifications"][0];
check!(
b,
"...and it is reported as a notification, ownerDisabledTransiently, with the read history",
n["kind"] == "notification"
&& n["ownerDisabledTransiently"] == true
&& n["ownerReads"].as_array().map(|a| a.len() >= 2 && a[0]["enabled"] == false
&& a.last().map(|l| l["enabled"] == true).unwrap_or(false)).unwrap_or(false),
s(&r["notifications"])
);
}
#[test]
fn an_owner_disabled_on_every_read_is_still_a_modal() {
let (b, r, _s, _m) = sweep_with_notice("settle-persistent", json!([false]), json!([]));
check!(
b,
"swx_close_all_safely: disabled on every re-read => stopped_by_dialog, as before",
r["errorCode"] == "stopped_by_dialog"
&& r["stoppedByDialog"]["ownerDisabledTransiently"] == false
&& r["stoppedByDialog"]["ownerReads"].as_array().map(|a| a.len() == 6).unwrap_or(false),
s(&r)
);
}
#[test]
fn a_window_with_a_button_is_never_settled() {
// Same evidence, plus a Button-class control: something is on offer. The
// owner turning enabled later does not make it a notification.
let (b, r, _s, m) =
sweep_with_notice("settle-button", json!([false, true]), json!([["Button", 0x50010000u64, "OK", true]]));
check!(
b,
"swx_close_all_safely: a window with a Button is a modal whatever its owner later reads",
r["errorCode"] == "stopped_by_dialog" && r["stoppedByDialog"]["ownerReads"].is_null(),
s(&r)
);
check!(b, "...and its owner was never re-read", !m.join("owner-reads-4329196.cursor").exists(), String::new());
}
/// A CONFIGURED LOG PATH THAT IS MISSING is unreadable, not absent (0.9.9,
/// adversary pass 45 V1): COM alone does not decide, and the refusal says why.
#[test]
fn a_missing_configured_log_is_unverifiable_not_absent() {
let scratch = TempDir::new("ready-log-missing");
session(&scratch, json!([]));
let missing = scratch.join("no-such-dir").join("SolidWorksPerformance.log");
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &missing.to_string_lossy())]);
b.call("swx_start", json!({}));
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"swx_new_document: refused, unverifiable, saying the configured log is missing",
r["errorCode"] == "host_app_starting"
&& r["startingUnverifiable"] == true
&& r["readiness"]["performanceLog"]["verdict"] == "unreadable"
&& r["error"].as_str().unwrap_or("").contains("performance log path is missing"),
s(&r)
);
}
// ── 0.9.11: MEASURED 2026-10-01 — a three-hour-old session locked out ──────
/// The performance log of Drew's session pid 26924 (started 14:59:33, 34
/// documents open, nearly three hours old), as measured — lines 93–101 add-in
/// loads (100 = the platform add-in), 106/107 the markers, 114–120 add-in
/// UNLOADS (119 = the platform add-in), 124 an on-demand load, then periodic
/// `<PROCMEM> CMD:AppRunTime` lines. 0.9.10 read `"UNLOAD_ADDIN"` as containing
/// `LOAD_ADDIN`, called line 119 the platform add-in LOADING after the marker —
/// "a start" — and refused every gated verb on this session.
fn drews_log() -> String {
let mut l = vec![
"; SolidWorks generated log file - do not edit.".to_string(),
"<VERSION> 6</VERSION>".into(),
"<PROC_NUM>16</PROC_NUM>".into(),
"<PROC_ARCH>9</PROC_ARCH>".into(),
"<OS>10.0.26100</OS>".into(),
];
for (dll, name) in [
("BendSequenceSwu.dll", "Bend Sequence"),
("SldAuraEx.DLL", "SOLIDWORKS Aura"),
("SWToolbox.dll", "SOLIDWORKS Toolbox Library"),
("SaveFolderTo3DEXPERIENCEAddin2.DLL", "Batch Save to 3DEXPERIENCE"),
("TaskPaneCacheExplorerAddin.DLL", "3DEXPERIENCE files on This PC"),
("SWK3DCompassAddin.dll", "3DEXPERIENCE for WORKS"),
] {
l.push(format!(r#"<AD2> "LOAD_ADDIN" "{dll}" "{name}" [2.18] "6.34.0.2027" 14 COM.new 1770885081 13"#));
}
l.push("<3DXINITERROR>0".into());
l.push("<3DXLOAD_TIME>19000</3DXLOAD_TIME>".into());
l.push(r#"<AD2> "UNLOAD_ADDIN" "BendSequenceSwu.dll" "" [0.00] 0"#.into());
l.push(r#"<AD2> "UNLOAD_ADDIN" "SWK3DCompassAddin.dll" "3DEXPERIENCE for WORKS" [0.50] 13"#.into());
l.push(r#"<AD2> "UNLOAD_ADDIN" "SldAuraEx.DLL" "" [0.00] 0"#.into());
l.push(r#"<AD2> "LOAD_ADDIN" "sldfuncfeat.dll" "sldFuncFeat" [0.16] "34.0" 14 COM.new 1770885300 13"#.into());
for _ in 0..4 {
l.push("<PROCMEM> CMD:AppRunTime 1832.5 12873 MB".into());
}
l.join("\n") + "\n"
}
fn created_ago(scratch: &TempDir, secs: u64) {
let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
std::fs::write(scratch.join("mock-sldworks-created"), (now - secs).to_string()).unwrap();
}
#[test]
fn an_unload_is_never_read_as_a_load_and_the_measured_log_is_ready() {
let scratch = TempDir::new("ready-drews-log");
session(&scratch, json!([]));
created_ago(&scratch, 3 * 3600);
let logp = write_log(&scratch, &drews_log(), 900);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let st = b.call("swx_start", json!({}));
check!(
b,
"swx_start on the measured log: ready, BY THE LOG (an on-demand add-in after the marker) — \
the platform add-in's UNLOAD is not a load",
st["ready"] == true
&& st["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete"
&& !st["readiness"]["basedOn"].to_string().contains("longRunningSession"),
s(&st["readiness"])
);
}
/// A SESSION CANNOT BE STARTING FOR HOURS (L2): the platform add-in loaded after
/// the marker (the shape of a start), COM says complete, and no startup line has
/// changed for five minutes — on a process three hours old that is ready
/// (`longRunningSession`); on one two minutes old it still holds.
#[test]
fn a_long_running_session_is_never_locked_out_by_a_log_rule() {
let text = "<LOAD_ADDIN> \"SWK3DCompassAddin.dll\"\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
<AD2> \"LOAD_ADDIN\" \"SWK3DCompassAddin.dll\" \"3DEXPERIENCE for WORKS\"\n";
let scratch = TempDir::new("ready-long-running");
session(&scratch, json!([]));
created_ago(&scratch, 3 * 3600);
let logp = write_log(&scratch, text, 300);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let st = b.call("swx_start", json!({}));
check!(
b,
"a three-hour-old session, COM true, startup lines quiet 300 s: ready, basis longRunningSession",
st["ready"] == true && st["readiness"]["basedOn"].to_string().contains("longRunningSession"),
s(&st["readiness"])
);
let scratch = TempDir::new("ready-young-holds");
session(&scratch, json!([]));
created_ago(&scratch, 120);
let logp = write_log(&scratch, text, 100);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let r = b.call("swx_new_document", json!({"type": "part"}));
check!(
b,
"the same log on a two-minute-old process: still loading, refused",
r["errorCode"] == "host_app_starting" && r["readiness"]["performanceLog"]["verdict"] == "loading",
s(&r)
);
}
/// PERIODIC RUN-TIME LINES ARE NOT STARTUP ACTIVITY (L3): an active session
/// keeps writing `<PROCMEM> CMD:AppRunTime`, so the FILE is never quiet. The
/// quiet window counts only add-in loads and markers.
#[test]
fn periodic_runtime_lines_do_not_keep_a_session_starting() {
let scratch = TempDir::new("ready-procmem");
session(&scratch, json!([]));
created_ago(&scratch, 300);
let base = "<LOAD_ADDIN> \"SWK3DCompassAddin.dll\"\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n\
<AD2> \"LOAD_ADDIN\" \"SWToolbox.dll\" \"SOLIDWORKS Toolbox Library\"\n\
<PROCMEM> CMD:AppRunTime 300.1 9000 MB\n";
let logp = write_log(&scratch, base, 0);
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_PERF_LOG", &logp), ("SWX_STARTUP_QUIET_SECS", "2")],
);
let first = b.call("swx_start", json!({}));
check!(b, "first look: the startup lines are new to the bridge — held", first["ready"] == false, s(&first["readiness"]));
std::thread::sleep(std::time::Duration::from_millis(2500));
// The session keeps working: another run-time line, written NOW.
write_log(&scratch, &format!("{base}<PROCMEM> CMD:AppRunTime 302.6 9001 MB\n"), 0);
let st = b.call("swx_status", json!({}));
check!(
b,
"a fresh PROCMEM line does not reset the quiet window: an on-demand add-in, ready",
st["solidworks"]["ready"] == true
&& st["solidworks"]["readiness"]["performanceLog"]["verdict"] == "onDemandAddInAfterComplete",
s(&st["solidworks"]["readiness"])
);
}
/// THE LATCH SURVIVES A BRIDGE RESPAWN (L5): every update respawns the bridge;
/// a session already seen ready stays ready under the new one, whatever its
/// log looks like now.
#[test]
fn a_session_seen_ready_stays_ready_across_a_bridge_restart() {
let scratch = TempDir::new("ready-latch-persist");
session(&scratch, json!([]));
created_ago(&scratch, 300);
let logp = write_log(&scratch, "<LOAD_ADDIN> x\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n", 0);
{
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let st = b.call("swx_start", json!({}));
check!(b, "precondition: ready under the first bridge", st["ready"] == true, s(&st["readiness"]));
}
// The update respawns the bridge; the log now looks like loading.
write_log(&scratch, "<LOAD_ADDIN> x\n<3DXLOAD_TIME>24000</3DXLOAD_TIME>\n<LOAD_ADDIN> \"SWK3DCompassAddin.dll\"\n", 0);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_PERF_LOG", &logp)]);
let st = b.call("swx_start", json!({}));
check!(
b,
"under the respawned bridge: still ready, because this session was",
st["ready"] == true && st["readiness"]["basedOn"].to_string().contains("readyBeforeThisBridgeRestarted"),
s(&st["readiness"])
);
}
/// swx_user_presence IS EXEMPT (L4) and works while SOLIDWORKS is starting:
/// foreground and last input are measured, the document part is unknown, and
/// unknown counts as present.
#[test]
fn user_presence_answers_during_startup_with_documents_unknown() {
let scratch = TempDir::new("ready-presence-starting");
session(&scratch, json!([]));
std::fs::write(scratch.join("mock-startup-incomplete"), "").unwrap();
let machine = a_visible_machine("ready-presence-starting-m");
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
b.call("swx_start", json!({}));
b.call("swx_status", json!({}));
let p = b.call("swx_user_presence", json!({}));
check!(
b,
"swx_user_presence during startup: answered, present, documents unknown, foreground measured",
p["success"] == true
&& p["present"] == true
&& p["evidence"]["documentsUnknown"] == true
&& p["evidence"]["foregroundWindow"].is_object(),
s(&p)
);
}