app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405
//! DREW'S STANDING AUTHORIZATION, 2026-09-11:
//!
//! "I despise every time you ask for help closing. You MUST find/build a way to
//! safely close the docs/program."
//!
//! The authorization is to CLOSE. It is not an authorization to LOSE anything,
//! and the difference is the whole of this file. Every test here asserts the same
//! sequence from a different angle:
//!
//! **nothing closes before it is on disk.**
//
// 0.9.10: the documents these sessions are seeded with were not opened by the
// bridge, so the user-presence guard counts them as the USER's (adversary pass
// 46: the baseline may contain only documents the bridge put there). Every
// sweep below passes `userPresentOverride` + `userAuthorized`, standing in for
// the user's explicit request — which is what these sweeps model.
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()
}
/// A session like the ones that have actually caused trouble: a document the
/// user saved and then edited, two pathless imports SHARING A TITLE (the state
/// that made every verb refuse with `ambiguous_target` live), an assembly, and a
/// component the assembly holds open.
/// A machine whose WINDOW CENSUS CAN BE READ. Without one the sweep correctly
/// preserves everything and closes nothing — a dialog nobody can see is the one
/// that matters — so every test about CLOSING has to supply a session the bridge
/// can look at.
fn a_visible_machine(tag: &str) -> TempDir {
let d = TempDir::new(tag);
std::fs::write(d.join("windows.json"), "[]").unwrap();
d
}
fn a_difficult_session(scratch: &TempDir) {
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 3, "active": "untitled:a",
"docs": [
// The user's own file, WITH unsaved changes. Their file on disk
// must not be touched; their changes must still survive.
{"docType": "part", "path": "C:\\Users\\drew\\work\\bracket.SLDPRT",
"title": "bracket.SLDPRT", "saved": true, "dirty": true},
// Two imports, no path, SAME TITLE.
{"docType": "part", "path": "untitled:a", "title": "First.step.SLDPRT",
"saved": false, "dirty": true},
{"docType": "part", "path": "untitled:b", "title": "First.step.SLDPRT",
"saved": false, "dirty": true},
// An assembly and a component it holds open.
{"docType": "assembly", "path": "C:\\Users\\drew\\work\\gearbox.SLDASM",
"title": "gearbox.SLDASM", "saved": true, "dirty": false},
{"docType": "part", "path": "C:\\Users\\drew\\work\\shaft.SLDPRT",
"title": "shaft.SLDPRT", "saved": true, "dirty": false,
"heldOpenByAssembly": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
}
#[test]
fn every_document_is_on_disk_before_anything_closes() {
let scratch = TempDir::new("shutdown-safe");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-safe-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
let before = b.call("swx_docs", json!({}));
check!(b, "precondition: a full session", before["count"].as_u64().unwrap_or(0) >= 4, s(&before));
// DEFAULT POSTURE. No write opt-in, no per-document confirmation: the user
// has already said this may happen. Only a reason.
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
check!(b, "the sweep succeeds in the default posture", r["success"] == true, s(&r));
check!(b, "...and finishes", r["done"] == true, s(&r));
// THE SAFETY PROPERTY: every document that was closed is on disk.
let recovered = r["recovered"].as_array().cloned().unwrap_or_default();
check!(b, "every document was accounted for", recovered.len() >= 4, s(&r));
for doc in &recovered {
let preserved = doc["preserved"].as_bool().unwrap_or(false);
check!(b, "...each one preserved", preserved, s(doc));
// A document that was dirty or pathless has a NEW file, and that file
// really exists.
if doc["wasDirty"] == true || doc["wasPathless"] == true {
let rp = doc["recoveryPath"].as_str().unwrap_or("");
check!(
b,
"...and a dirty or pathless document was written to recovery",
!rp.is_empty() && std::path::Path::new(rp).exists(),
s(doc)
);
}
}
// THE USER'S OWN FILE WAS NOT WRITTEN OVER: their dirty document went to
// recovery under a different path.
let theirs = recovered
.iter()
.find(|d| d["title"] == "bracket.SLDPRT")
.cloned()
.unwrap_or(json!({}));
check!(
b,
"the user's dirty file was copied to recovery, NOT saved over",
theirs["recoveryPath"].as_str().unwrap_or("") != "C:\\Users\\drew\\work\\bracket.SLDPRT"
&& theirs["recoveryPath"].as_str().unwrap_or("").contains("recovery"),
s(&theirs)
);
// TWO DOCUMENTS SHARING A TITLE each got their own file.
let paths: Vec<String> = recovered
.iter()
.filter(|d| d["title"] == "First.step.SLDPRT")
.map(|d| d["recoveryPath"].as_str().unwrap_or("").to_string())
.collect();
check!(
b,
"same-titled pathless documents each get a UNIQUE recovery file",
paths.len() == 2 && paths[0] != paths[1] && !paths[0].is_empty(),
format!("{paths:?}")
);
// AND THE COUNT REACHES ZERO — including the component the assembly held.
let after = b.call("swx_docs", json!({}));
check!(b, "the session is empty", after["count"] == 0, s(&after));
}
/// A document that could NOT be written is not closed. This is the case the whole
/// design exists to get right: closing it would discard the only copy.
#[test]
fn a_document_that_cannot_be_saved_is_not_closed() {
let scratch = TempDir::new("shutdown-nosave");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-nosave-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-saveas-fails"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "the sweep does NOT report itself done", r["done"] != true, s(&r));
let remaining = r["remaining"].as_array().cloned().unwrap_or_default();
check!(b, "...and names what it could not preserve", !remaining.is_empty(), s(&r));
check!(
b,
"...saying why in terms of the harm",
remaining[0]["why"].as_str().unwrap_or("").contains("NOT closed"),
s(&remaining[0])
);
// The dirty documents are STILL OPEN: unsaved work is never discarded.
let after = b.call("swx_docs", json!({}));
check!(b, "the unsaved documents are still open", after["count"].as_u64().unwrap_or(0) > 0, s(&after));
}
/// A dialog stops the sweep and is never answered. A save prompt should be
/// impossible here — everything is clean before anything closes — so one
/// appearing means a premise is wrong.
#[test]
fn a_dialog_stops_the_sweep_and_is_never_answered() {
let machine = TempDir::new("shutdown-dialog-machine");
std::fs::write(
machine.join("dialog-open.json"),
json!({"hwnd": 9001, "className": "#32770", "title": "Save Modified Documents",
"visible": true})
.to_string(),
)
.unwrap();
let scratch = TempDir::new("shutdown-dialog");
a_difficult_session(&scratch);
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": "closing"}));
check!(
b,
"the sweep STOPS on a dialog",
r["success"] == false && r["errorCode"] == "stopped_by_dialog",
s(&r)
);
check!(
b,
"...names it",
s(&r).contains("Save Modified Documents"),
s(&r)
);
check!(
b,
"...and says it was not answered",
r["_hint"].as_str().unwrap_or("").contains("never answers"),
s(&r)
);
// NOTHING was closed.
let after = b.call("swx_docs", json!({}));
check!(b, "nothing was closed", after["count"].as_u64().unwrap_or(0) >= 4, s(&after));
}
/// The whole intention in one verb — and the quit happens only because the sweep
/// really emptied the session.
#[test]
fn swx_shutdown_preserves_everything_then_quits() {
let machine = TempDir::new("shutdown-machine");
std::fs::write(machine.join("quit-script.json"), r#"{"a":"exited"}"#).unwrap();
let scratch = TempDir::new("shutdown-full");
a_difficult_session(&scratch);
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[
("SWX_LAUNCH_FAKE_DIR", &machine.path()),
("SWX_QUIT_SETTLE_MS", "80"),
("SWX_QUIT_EXIT_WAIT_SECS", "3"),
],
);
b.call("swx_start", json!({}));
// NO write opt-in and NO launch attribution: the user's authorization is what
// makes this allowed, and this bridge did not launch the session.
let r = b.call("swx_shutdown", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close SOLIDWORKS"}));
check!(b, "the shutdown succeeds", r["success"] == true, s(&r));
check!(b, "...having preserved every document", r["recovered"].as_array().map(|a| a.len() >= 4).unwrap_or(false), s(&r));
check!(b, "...emptied the session", r["done"] == true, s(&r));
check!(b, "...and quit SOLIDWORKS", r["stopped"] == true, s(&r));
// IDEMPOTENT: the last thing a caller does should be safe to repeat.
let again = b.call("swx_shutdown", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "making sure"}));
check!(
b,
"a second shutdown is a no-op, not a failure",
again["success"] == true && again["alreadyStopped"] == true,
s(&again)
);
}
/// What was preserved can be found again — otherwise "nothing is lost" is a claim
/// nobody can act on.
#[test]
fn swx_recover_list_finds_what_was_preserved() {
let scratch = TempDir::new("shutdown-recover");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-recover-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
let swept = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "precondition: the sweep ran", swept["done"] == true, s(&swept));
let r = b.call("swx_recover_list", json!({}));
let sweeps = r["sweeps"].as_array().cloned().unwrap_or_default();
check!(b, "the sweep is listed", !sweeps.is_empty(), s(&r));
let files = sweeps[0]["files"].as_array().cloned().unwrap_or_default();
check!(b, "...with the files it preserved", files.len() >= 3, s(&sweeps[0]));
check!(
b,
"...each really on disk",
files.iter().all(|f| std::path::Path::new(f["path"].as_str().unwrap_or("")).exists()),
s(&files[0])
);
}
/// A DIALOG NOBODY CAN SEE. If the window census cannot be read, this bridge
/// cannot say whether a save prompt is on screen — and the two halves of the
/// sweep answer that differently. Saving loses nothing, so the saves go ahead;
/// closing while blind can lose everything, so nothing closes. The failure mode
/// this pins is the project's oldest: an unanswerable question read as
/// permission.
#[test]
fn an_unreadable_dialog_census_preserves_but_closes_nothing() {
let scratch = TempDir::new("shutdown-blind");
a_difficult_session(&scratch);
// NO fake machine: there is no session this process can look at, so the
// census is UNKNOWN — not empty.
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({}));
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(
b,
"a census nobody can read stops the closes",
r["success"] == false && r["errorCode"] == "dialog_census_unknown",
s(&r)
);
check!(b, "...says so in one word", r["stoppedBy"] == "dialogCensusUnknown", s(&r));
check!(b, "...and does not claim a check it never made", r["dialogCensusReadable"] == false, s(&r));
check!(b, "...nor that it finished", r["done"] != true, s(&r));
// THE SAVES WENT AHEAD — and really wrote files.
let recovered = r["recovered"].as_array().cloned().unwrap_or_default();
let written: Vec<&Value> = recovered
.iter()
.filter(|d| d["preserved"] == true && d["recoveryPath"].is_string())
.collect();
check!(b, "the work was preserved anyway", written.len() >= 3, s(&r));
for d in &written {
let rp = d["recoveryPath"].as_str().unwrap_or("");
let bytes = d["recoveryBytes"].as_u64().unwrap_or(0);
check!(
b,
"...each recovery file exists, holds a real document's worth of bytes, \
and carries the OLE compound-document signature",
std::path::Path::new(rp).exists()
&& bytes >= 16 * 1024
&& d["recoverySignatureOk"] == true,
s(d)
);
}
// AND NOTHING WAS CLOSED.
check!(b, "no document was closed", r["closed"].as_u64().unwrap_or(99) == 0, s(&r));
let after = b.call("swx_docs", json!({}));
check!(b, "the session is untouched", after["count"].as_u64().unwrap_or(0) >= 4, s(&after));
}
/// THE USER'S OWN FILE IS NEVER WRITTEN OVER, byte for byte.
///
/// `SaveAs3(path, ver, options=1)` with `swSaveAsOptions_Silent` ALONE — no
/// `Copy` bit — RE-POINTS the document at the new file. That is deliberate: the
/// document must become the recovery file, so the close that follows closes a
/// document whose only content is already on disk. The cost of re-pointing is
/// that the user's original is left exactly as it was, untouched, which is what
/// this test reads back one byte at a time. The journal supplies the other half:
/// the path closed was the RECOVERY path, so the re-point really happened before
/// the close.
#[test]
fn the_users_original_file_is_untouched_and_the_document_re_points() {
let scratch = TempDir::new("shutdown-original");
// A REAL FILE ON DISK holding known bytes, so "unchanged" is checkable.
let theirs = scratch.join("bracket.SLDPRT");
let original: Vec<u8> = (0u16..2048).map(|i| (i % 251) as u8).collect();
std::fs::write(&theirs, &original).unwrap();
let their_path = theirs.to_string_lossy().to_string();
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 0, "active": their_path,
"docs": [
{"docType": "part", "path": their_path, "title": "bracket.SLDPRT",
"saved": true, "dirty": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-original-machine");
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": "closing"}));
check!(b, "the sweep finished", r["success"] == true && r["done"] == true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let theirs_rec = rec.iter().find(|d| d["title"] == "bracket.SLDPRT").cloned().unwrap_or(json!({}));
let rp = theirs_rec["recoveryPath"].as_str().unwrap_or("").to_string();
check!(b, "their document went to a DIFFERENT file", !rp.is_empty() && rp != their_path, s(&theirs_rec));
// THE ORIGINAL BYTES, unchanged.
let now = std::fs::read(&theirs).unwrap_or_default();
check!(
b,
"the user's original file is byte-for-byte what it was",
now == original,
format!("{} bytes before, {} after", original.len(), now.len())
);
// THE RE-POINT: what was closed was the recovery file, not their original.
let log = std::fs::read_to_string(scratch.join("mock-close-order.log")).unwrap_or_default();
check!(
b,
"the CLOSE key was the recovery path — the document re-pointed",
log.lines().any(|l| l == format!("closed\t{rp}")),
log.clone()
);
check!(
b,
"...and their original path was never closed, because it was never the document any more",
!log.lines().any(|l| l == format!("closed\t{their_path}")),
log
);
}
/// ORDERING, on the case that makes ordering matter: an assembly holding a DIRTY
/// component open. The component cannot close while its parent holds it, and its
/// unsaved changes exist only in memory — so the sweep must write it FIRST and
/// close the parent before the child. This asserts the order of events, not the
/// final state: a session can end with everything on disk and still have closed
/// something before it was written.
#[test]
fn no_component_closes_before_its_own_recovery_write() {
let scratch = TempDir::new("shutdown-ordering");
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 0, "active": "C:\\Users\\drew\\work\\rig.SLDASM",
"docs": [
// The parent, itself dirty.
{"docType": "assembly", "path": "C:\\Users\\drew\\work\\rig.SLDASM",
"title": "rig.SLDASM", "saved": true, "dirty": true},
// TWO DIRTY COMPONENTS the assembly holds open. Their changes are
// in memory only; closing the parent takes them with it.
{"docType": "part", "path": "C:\\Users\\drew\\work\\arm.SLDPRT",
"title": "arm.SLDPRT", "saved": true, "dirty": true,
"heldOpenByAssembly": true},
{"docType": "part", "path": "", "title": "lever.SLDPRT",
"saved": false, "dirty": true, "heldOpenByAssembly": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-ordering-machine");
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": "closing"}));
check!(b, "the sweep finished", r["success"] == true && r["done"] == true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
check!(b, "all three documents were preserved", rec.len() == 3, s(&r));
for d in &rec {
check!(
b,
"...each with a real document on disk, signature and all",
d["recoveryBytes"].as_u64().unwrap_or(0) >= 16 * 1024
&& d["recoverySignatureOk"] == true,
s(d)
);
}
// THE JOURNAL. Every close must come after every write it depended on.
let log = std::fs::read_to_string(scratch.join("mock-close-order.log")).unwrap_or_default();
let lines: Vec<(&str, &str)> = log
.lines()
.filter_map(|l| l.split_once('\t'))
.collect();
let first_close = lines.iter().position(|(e, _)| *e == "closed");
let last_write = lines.iter().rposition(|(e, _)| *e == "preserved");
check!(
b,
"EVERY recovery write happened before the FIRST close",
matches!((first_close, last_write), (Some(c), Some(w)) if w < c),
log.clone()
);
// Each component's OWN write precedes its OWN close.
for d in &rec {
let rp = d["recoveryPath"].as_str().unwrap_or("").to_string();
let wrote = lines.iter().position(|(e, p)| *e == "preserved" && *p == rp);
let closed = lines.iter().position(|(e, p)| *e == "closed" && *p == rp);
check!(
b,
"...and this document was written before it was closed",
matches!((wrote, closed), (Some(w), Some(c)) if w < c),
format!("{}\n{log}", s(d))
);
}
// THE PARENT GOES FIRST: a child closed before its parent would have been
// refused by SOLIDWORKS, which is the reason the order is what it is.
let closes: Vec<&str> = lines.iter().filter(|(e, _)| *e == "closed").map(|(_, p)| *p).collect();
let asm = closes.iter().position(|p| p.ends_with(".SLDASM"));
check!(
b,
"the assembly closed before its components",
asm == Some(0) && closes.len() == 3,
format!("{closes:?}")
);
// AND NO CLOSE WAS EVER REFUSED. This is the assertion that makes the order
// matter rather than the outcome: closing a child first still finishes, because
// the sweep retries and the parent goes on the next pass — but SOLIDWORKS had
// to say no first, and being told no is the thing we are avoiding.
check!(
b,
"no close was attempted that SOLIDWORKS would refuse",
!log.lines().any(|l| l.starts_with("refused\t")),
log
);
let after = b.call("swx_docs", json!({}));
check!(b, "the session is empty", after["count"] == 0, s(&after));
}
/// A RECOVERY FILE THAT HOLDS NOTHING IS NOT A SAVE. `Path::exists()` answers yes
/// to the zero-byte file a full disk leaves behind, and "the file is there" was
/// the whole premise for closing the document. So the write is verified by SIZE,
/// through the same constant on both backends, and a short file leaves the
/// document OPEN.
#[test]
fn a_zero_byte_recovery_file_does_not_authorise_a_close() {
let scratch = TempDir::new("shutdown-partial");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-partial-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
// The disk fills: SaveAs3 "succeeds", the file appears, and it is empty.
std::fs::write(b.scratch.join("mock-recovery-partial-write"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "the sweep does not report itself done", r["done"] != true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let short: Vec<&Value> = rec.iter().filter(|d| d["preserved"] == false).collect();
check!(b, "the documents it could not preserve are named", short.len() >= 3, s(&r));
for d in &short {
// THE FILE IS THERE. Existence would have said yes.
let rp = d["recoveryPath"].as_str().unwrap_or("");
check!(b, "...the empty file really exists", std::path::Path::new(rp).exists(), s(d));
check!(b, "...and the size is reported, not just a verdict", d["recoveryBytes"] == 0, s(d));
check!(
b,
"...saying why in terms of the harm",
d["why"].as_str().unwrap_or("").contains("NOT closed"),
s(d)
);
}
// NOTHING that was only in memory was closed.
let after = b.call("swx_docs", json!({}));
check!(
b,
"every document whose only copy was in memory is still open",
after["count"].as_u64().unwrap_or(0) >= 3,
s(&after)
);
}
/// A TRUNCATED FILE IS NOT A SAVE — and this is the case the 1 KB floor of 0.7.1
/// admitted. A partial write on a full disk does not usually stop at zero bytes;
/// it writes a genuine header and then stops, at 2 KB or 8 KB or 30 KB. Every one
/// of those passed the old floor, was recorded `preserved:true`, and the document
/// was closed against it.
#[test]
fn a_truncated_recovery_file_does_not_authorise_a_close() {
let scratch = TempDir::new("shutdown-truncated");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-truncated-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
// A real header, and then the disk fills.
std::fs::write(b.scratch.join("mock-recovery-truncated"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "the sweep does not report itself done", r["done"] != true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let short: Vec<&Value> = rec.iter().filter(|d| d["preserved"] == false).collect();
check!(b, "the truncated documents are named", short.len() >= 3, s(&r));
for d in &short {
// THE SIGNATURE IS RIGHT. Only the size is wrong — which is exactly why
// the size check has to survive alongside the structural one.
check!(b, "...the header was genuine", d["recoverySignatureOk"] == true, s(d));
let bytes = d["recoveryBytes"].as_u64().unwrap_or(0);
check!(b, "...the file was NOT empty", bytes > 0 && bytes < 16 * 1024, s(d));
check!(
b,
"...and it says the file was short, in terms of the harm",
d["why"].as_str().unwrap_or("").contains("NOT closed"),
s(d)
);
}
let after = b.call("swx_docs", json!({}));
check!(
b,
"every document whose only copy was in memory is still open",
after["count"].as_u64().unwrap_or(0) >= 3,
s(&after)
);
}
/// A FILE THAT IS NOT A SOLIDWORKS DOCUMENT DOES NOT AUTHORISE A CLOSE, whatever
/// its size. SOLIDWORKS documents are OLE compound files: eight bytes at offset 0
/// settle it structurally, where a byte count can only guess. The check FAILS
/// CLOSED, so a future container change would make this bridge refuse to close
/// rather than close against files it cannot vouch for.
#[test]
fn a_recovery_file_with_the_wrong_signature_does_not_authorise_a_close() {
let scratch = TempDir::new("shutdown-header");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-header-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
// Full size, wrong container.
std::fs::write(b.scratch.join("mock-recovery-bad-header"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "the sweep does not report itself done", r["done"] != true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let bad: Vec<&Value> = rec.iter().filter(|d| d["preserved"] == false).collect();
check!(b, "the unrecognised files are named", bad.len() >= 3, s(&r));
for d in &bad {
check!(b, "...the signature is reported, not just a verdict", d["recoverySignatureOk"] == false, s(d));
// SIZE ALONE WOULD HAVE PASSED IT.
check!(
b,
"...and the file was big enough that a byte floor would have said yes",
d["recoveryBytes"].as_u64().unwrap_or(0) >= 16 * 1024,
s(d)
);
check!(
b,
"...saying what it found and what it expected",
d["why"].as_str().unwrap_or("").contains("D0 CF 11 E0"),
s(d)
);
}
let after = b.call("swx_docs", json!({}));
check!(b, "nothing was closed against a file we cannot vouch for",
after["count"].as_u64().unwrap_or(0) >= 3, s(&after));
}
/// A DIALOG RAISED BY A SAVE STOPS THE CLOSES.
///
/// This is the whole reason the census moved. A save is not a silent operation:
/// it can raise a missing-external-reference or rebuild prompt of its own. 0.7.1
/// censused once per BATCH, so a dialog raised by the first save was invisible
/// until every other document in the batch had already been closed — the sweep
/// carried on working behind a modal window it had itself produced.
#[test]
fn a_dialog_raised_by_a_save_stops_the_closes() {
let scratch = TempDir::new("shutdown-midsweep");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-midsweep-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
let before = b.call("swx_docs", json!({}));
let n = before["count"].as_u64().unwrap_or(0);
check!(b, "precondition: a full session", n >= 4, s(&before));
// The FIRST save puts a prompt on screen.
std::fs::write(b.scratch.join("mock-dialog-after-save"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(
b,
"the sweep STOPS on a dialog a SAVE raised",
r["success"] == false && r["errorCode"] == "stopped_by_dialog",
s(&r)
);
check!(b, "...and names it", s(&r).contains("Save external references?"), s(&r));
check!(b, "...and never answered it", r["_hint"].as_str().unwrap_or("").contains("never answers"), s(&r));
// THE SWEEP STOPPED SAVING TOO, and this is the half only a per-SAVE census
// can protect. Exactly one document was written — the one whose save raised
// the prompt — and the bridge did not go on working behind a modal window it
// had itself produced. A census that runs once per pass would have saved
// every document in the pass before looking again.
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
check!(
b,
"it stopped at the document whose save raised the dialog",
rec.len() == 1,
s(&r)
);
// AND NOTHING WAS CLOSED.
check!(b, "no document was closed", r["closed"].as_u64().unwrap_or(99) == 0, s(&r));
let after = b.call("swx_docs", json!({}));
check!(
b,
"the session still holds every document it held",
after["count"].as_u64().unwrap_or(0) == n,
s(&after)
);
}
/// AND A DIALOG RAISED BY A CLOSE STOPS THE REST OF THEM. The census runs before
/// EVERY close, not once before the run of them: whatever a close puts on screen
/// is seen before the next one is attempted, and the documents that had not been
/// reached yet are left open with their work already safe on disk.
#[test]
fn a_dialog_raised_by_a_close_stops_the_remaining_closes() {
let scratch = TempDir::new("shutdown-midclose");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-midclose-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
let before = b.call("swx_docs", json!({}));
let n = before["count"].as_u64().unwrap_or(0);
check!(b, "precondition: a full session", n >= 4, s(&before));
std::fs::write(b.scratch.join("mock-dialog-during-close"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(
b,
"the sweep STOPS on a dialog a CLOSE raised",
r["success"] == false && r["errorCode"] == "stopped_by_dialog",
s(&r)
);
check!(b, "...and names it", s(&r).contains("Rebuild before closing?"), s(&r));
// EVERYTHING WAS PRESERVED FIRST — the stop costs the user nothing.
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
check!(
b,
"every document was preserved before any of this",
rec.len() >= 4 && rec.iter().all(|d| d["preserved"] == true),
s(&r)
);
// AND THE SWEEP STOPPED PART-WAY, which is the point: it did not run the
// whole list and look afterwards.
let closed = r["closed"].as_u64().unwrap_or(0);
check!(b, "it closed something, but not everything", closed >= 1 && closed < n, s(&r));
let after = b.call("swx_docs", json!({}));
check!(
b,
"the documents it had not reached are still open",
after["count"].as_u64().unwrap_or(0) > 0,
s(&after)
);
}
/// REAL SOLIDWORKS FILES ARE ACCEPTED. This is the test the 0.7.2 spec needed and
/// did not have.
///
/// Pass 23 asked for the recovery file to be verified against the OLE
/// compound-document signature `D0 CF 11 E0 A1 B1 1A E1` — the answer every
/// reference gives for "what is a SLDPRT". Measured against 166 real
/// `.SLDPRT`/`.SLDASM`/`.SLDDRW` files from three hardware repositories on this
/// machine, **none** of them begins with it, and it does not appear anywhere in
/// the first 4 MB of any of them. All 166 carry `00 00 00 04` at offset 4
/// instead. Shipping the signature as specified would have rejected every
/// document on a modern install: failing closed, so nothing lost — and the sweep
/// would have preserved everything and closed nothing, for ever.
///
/// The headers below are the first bytes of real files, copied verbatim. A check
/// on a format this bridge does not own has to be pinned to samples of the real
/// thing, or the next well-sourced guess ships too.
#[test]
fn the_container_check_accepts_real_solidworks_documents() {
use swx_bridge::comhost::host::recovery_file_verdict;
use swx_bridge::disk::DocKind;
// The KIND is what the backend established, which for these fixtures is the
// kind the file name states. It is passed in, never read back off the name
// inside the verdict — that circularity is what 0.7.5 removed.
let kind_of = |name: &str| DocKind::from_extension(name);
let dir = TempDir::new("containers");
// (name, first 16 bytes as they appear on disk, what it is)
let real: &[(&str, [u8; 16])] = &[
("drawing.SLDDRW",
[0x63,0xf7,0xbf,0x10, 0x00,0x00,0x00,0x04, 0x10,0x75,0x2c,0xc4, 0x74,0xe9,0x14,0x00]),
("part.SLDPRT",
[0x72,0x61,0xbe,0x5c, 0x00,0x00,0x00,0x04, 0x8a,0x8a,0x3f,0xf4, 0x8c,0x14,0x00,0x06]),
("part2.SLDPRT",
[0xb8,0x79,0xb5,0xb6, 0x00,0x00,0x00,0x04, 0x8c,0x0b,0x87,0x9d, 0x6c,0xbd,0xba,0x63]),
("assembly.SLDASM",
[0xbe,0x00,0x56,0x2f, 0x00,0x00,0x00,0x04, 0x10,0x45,0x18,0x78, 0x3f,0xe6,0xef,0x0a]),
("assembly2.SLDASM",
[0x45,0x01,0x95,0xb3, 0x00,0x00,0x00,0x04, 0x89,0x56,0xde,0xec, 0x80,0x79,0xa5,0x4c]),
];
for (name, head) in real {
let p = dir.join(name);
let mut body = head.to_vec();
// EACH KIND AT A PLAUSIBLE SIZE FOR ITS KIND. The floors differ because
// the real files do.
body.resize(
if name.ends_with("SLDASM") {
128 * 1024
} else if name.ends_with("SLDDRW") {
512 * 1024
} else {
64 * 1024
},
b'x',
);
std::fs::write(&p, &body).unwrap();
let v = recovery_file_verdict(&p.to_string_lossy(), kind_of(name), None);
assert!(
v.ok && v.signature_ok && v.container == Some("swdoc"),
"a REAL SOLIDWORKS file was refused: {name} -> ok={} container={:?} why={:?}\n\
Every document on a modern install looks like this. A check that rejects it \
fails closed, which is safe — and leaves the bridge unable to close anything, \
which is the whole feature.",
v.ok, v.container, v.why
);
}
// The LEGACY container is still a document: an older file from an archive.
let old = dir.join("legacy.SLDPRT");
let mut body = vec![0xD0u8, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
body.resize(64 * 1024, b'x');
std::fs::write(&old, &body).unwrap();
let v = recovery_file_verdict(&old.to_string_lossy(), Some(DocKind::Part), None);
assert!(v.ok && v.container == Some("ole-compound"), "legacy compound file refused: {v:?}");
// AND THE THINGS THAT ARE NOT DOCUMENTS ARE STILL REFUSED — the check has to
// keep discriminating, not just say yes.
let junk = dir.join("junk.SLDPRT");
std::fs::write(&junk, vec![b'x'; 64 * 1024]).unwrap();
let v = recovery_file_verdict(&junk.to_string_lossy(), Some(DocKind::Part), None);
assert!(!v.ok && !v.signature_ok, "a file that is not a document was accepted: {v:?}");
let short = dir.join("short.SLDPRT");
let mut body = real[0].1.to_vec();
body.resize(2048, b'x');
std::fs::write(&short, &body).unwrap();
let v = recovery_file_verdict(&short.to_string_lossy(), Some(DocKind::Part), None);
assert!(
!v.ok && v.signature_ok && v.bytes == 2048,
"a truncated document was accepted: {v:?}"
);
let missing =
recovery_file_verdict(&dir.join("nothing-here.SLDPRT").to_string_lossy(), Some(DocKind::Part), None);
assert!(!missing.ok, "a file that does not exist was accepted: {missing:?}");
// THE FLOOR IS PER KIND, and this is what one floor for all three would have
// cost: 32 KB is a plausible part and a badly truncated assembly. The
// smallest real assembly measured here is 95,492 bytes — three times the
// smallest part — so an assembly that stops at 32 KB has lost most of itself
// while still carrying the marker and clearing any part-sized floor.
for (name, ok_expected) in [("thirty-two.SLDPRT", true), ("thirty-two.SLDASM", false)] {
let p = dir.join(name);
let mut body = real[1].1.to_vec();
body.resize(32 * 1024, b'x');
std::fs::write(&p, &body).unwrap();
let v = recovery_file_verdict(&p.to_string_lossy(), kind_of(name), None);
assert_eq!(
v.ok, ok_expected,
"a 32 KB {name} was judged ok={} — the floors must differ by kind, or an assembly \
is closed against a third of itself: {v:?}",
v.ok
);
}
}
/// An older document really can be closed: the legacy container is accepted end
/// to end, not just by the verdict function.
#[test]
fn a_legacy_compound_document_is_preserved_and_closed() {
let scratch = TempDir::new("shutdown-legacy");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-legacy-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-recovery-legacy-container"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "the sweep finished", r["success"] == true && r["done"] == true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
check!(
b,
"every document was preserved and vouched for",
rec.iter().all(|d| d["preserved"] == true),
s(&r)
);
let after = b.call("swx_docs", json!({}));
check!(b, "the session is empty", after["count"] == 0, s(&after));
}
/// A SWEEP THAT CANNOT FIT DOES NOT START.
///
/// This is the answer to the failure marker-and-floor cannot see: THE RIGHT FILE,
/// CUT SHORT. The container marker is at offset 4, so every prefix of a real
/// document carries it, and a truncation past 16 KB clears the size floor too —
/// a 20 KB prefix of a 500 KB assembly passes both checks and the document is
/// closed against it. Nothing in the file would show it. So the defence is the
/// decision to begin: a truncation this bridge could not detect afterwards is one
/// it declines to cause.
#[test]
fn a_sweep_that_cannot_fit_refuses_to_start() {
let scratch = TempDir::new("shutdown-nospace");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-nospace-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[
("SWX_LAUNCH_FAKE_DIR", &machine.path()),
// ROOM FOR ONE DOCUMENT, BUT NOT FOR THE SESSION. This is the case
// only the gate before the sweep can catch: every individual save
// would pass a per-save check and the sweep would get several
// documents in before discovering it cannot finish — having already
// written files it may not be able to complete.
("SWX_TEST_FREE_BYTES", "3000000"),
],
);
b.call("swx_start", json!({}));
let before = b.call("swx_docs", json!({}));
let n = before["count"].as_u64().unwrap_or(0);
check!(b, "precondition: a full session", n >= 4, s(&before));
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(
b,
"the sweep refuses to begin",
r["success"] == false && r["errorCode"] == "insufficient_headroom",
s(&r)
);
check!(b, "...in one word a caller can branch on", r["stoppedBy"] == "insufficientHeadroom", s(&r));
check!(b, "...and does not claim to have finished", r["done"] != true, s(&r));
// THE NUMBERS ARE NAMED — free, needed, and the shortfall between them.
check!(b, "it says how much room there was", r["headroomBytesBefore"] == 3_000_000, s(&r));
check!(
b,
"...and that it refused BEFORE touching anything, not partway through",
r["stoppedByHeadroom"]["when"] == "before anything was written",
s(&r)
);
check!(
b,
"...the session as a whole is what did not fit",
r["stoppedByHeadroom"]["neededBytes"].as_u64().unwrap_or(0) > 3_000_000,
s(&r)
);
let est = r["estimatedWriteBytes"].as_u64().unwrap_or(0);
check!(b, "...and what it was about to write", est > 1024, s(&r));
check!(
b,
"...and the shortfall, without making the caller subtract",
r["stoppedByHeadroom"]["shortfallBytes"].as_u64().unwrap_or(0) > 0,
s(&r)
);
// NOTHING WAS WRITTEN AND NOTHING WAS CLOSED.
check!(b, "nothing was preserved", r["recovered"].as_array().map(|a| a.is_empty()).unwrap_or(false), s(&r));
check!(b, "nothing was closed", r["closed"].as_u64().unwrap_or(99) == 0, s(&r));
let after = b.call("swx_docs", json!({}));
check!(b, "the session is untouched", after["count"].as_u64().unwrap_or(0) == n, s(&after));
// AND NO RECOVERY FILE WAS LEFT BEHIND to be mistaken for a rescue.
let dir = scratch.join("recovery");
let files: Vec<_> = std::fs::read_dir(&dir)
.map(|rd| rd.flatten().map(|e| e.path()).collect())
.unwrap_or_default();
check!(b, "no half-written rescue was left on disk", files.is_empty(), format!("{files:?}"));
}
/// AND A DISK THAT FILLS MID-SWEEP STOPS IT. The check before the first save is
/// not enough: this sweep is itself one of the things filling the volume, so the
/// question is asked again before EVERY save — the one that would be truncated,
/// not the first of them.
#[test]
fn a_disk_that_fills_mid_sweep_stops_the_sweep() {
let scratch = TempDir::new("shutdown-filling");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-filling-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[
("SWX_LAUNCH_FAKE_DIR", &machine.path()),
// Room to start, room for the first save, and then the volume fills.
("SWX_TEST_FREE_BYTES", "50000000000,50000000000,2048"),
],
);
b.call("swx_start", json!({}));
let before = b.call("swx_docs", json!({}));
let n = before["count"].as_u64().unwrap_or(0);
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(
b,
"the sweep stops when the volume fills under it",
r["success"] == false && r["errorCode"] == "insufficient_headroom",
s(&r)
);
check!(b, "...and does not claim to have finished", r["done"] != true, s(&r));
check!(
b,
"...saying WHEN it stopped, not just that it did",
r["stoppedByHeadroom"]["when"].as_str().unwrap_or("").contains("after preserving"),
s(&r)
);
// IT GOT SOMEWHERE FIRST, and what it wrote is real and still there.
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
check!(b, "it preserved what it could before stopping", rec.len() >= 1, s(&r));
for d in &rec {
if let Some(rp) = d["recoveryPath"].as_str() {
check!(
b,
"...and that work is really on disk",
std::path::Path::new(rp).exists() && d["recoverySignatureOk"] == true,
s(d)
);
}
}
// BUT NOTHING WAS CLOSED: a sweep that cannot finish preserving must not
// start closing.
check!(b, "nothing was closed", r["closed"].as_u64().unwrap_or(99) == 0, s(&r));
let after = b.call("swx_docs", json!({}));
check!(b, "every document is still open", after["count"].as_u64().unwrap_or(0) == n, s(&after));
}
/// THE NUMBERS ARE ALWAYS REPORTED, including on the happy path. A sweep that
/// writes every open document to disk and never says how much room it had is
/// asking to be trusted about the one thing it cannot verify afterwards.
#[test]
fn a_successful_sweep_still_reports_the_room_it_had() {
let scratch = TempDir::new("shutdown-headroom-ok");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-headroom-ok-machine");
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": "closing"}));
check!(b, "the sweep finished", r["success"] == true && r["done"] == true, s(&r));
check!(
b,
"it reports the free space it measured on a REAL volume",
r["headroomBytesBefore"].as_u64().unwrap_or(0) > 0,
s(&r)
);
check!(
b,
"...and what it estimated it would write",
r["estimatedWriteBytes"].as_u64().unwrap_or(0) > 0,
s(&r)
);
check!(
b,
"...and how that estimate was arrived at, since most of it is estimated",
r["estimatedFrom"]["documents"].as_u64().unwrap_or(0) >= 4
&& r["estimatedFrom"]["estimated"].as_u64().unwrap_or(0) >= 1,
s(&r)
);
}
/// THE WIRE: the document's real TYPE decides the recovery file's name AND the
/// floor it is verified against.
///
/// This is the chain pass 26 broke open, asserted end to end rather than in
/// pieces. A dirty assembly must be written as `.SLDASM` and held to the
/// assembly floor. Break the type-to-extension map (`Assembly => "SLDPRT"`) and
/// this fails — which is what makes it a test of the WIRE and not of either end.
#[test]
fn an_assemblys_type_decides_both_its_recovery_name_and_its_floor() {
let scratch = TempDir::new("shutdown-kindwire");
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 1, "active": "untitled:a",
"docs": [
// A DIRTY, PATHLESS ASSEMBLY: nothing but its type says what it
// is — no path, and a title SOLIDWORKS made up.
{"docType": "assembly", "path": "untitled:a", "title": "Assem1",
"saved": false, "dirty": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-kindwire-machine");
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": "closing"}));
check!(b, "the sweep finished", r["success"] == true && r["done"] == true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let d = rec.first().cloned().unwrap_or(json!({}));
check!(b, "the document was preserved", d["preserved"] == true, s(&r));
// THE TYPE REACHED THE NAME.
let rp = d["recoveryPath"].as_str().unwrap_or("").to_string();
check!(b, "an assembly is written as an assembly", rp.ends_with(".SLDASM"), s(&d));
// THE TYPE REACHED THE REPORT.
check!(b, "and the kind it was judged as is published", d["docKind"] == "assembly", s(&d));
// AND THE ASSEMBLY FLOOR WAS THE ONE APPLIED: the file clears 80 KB, which a
// part-sized rescue of the same document would not have.
check!(
b,
"an assembly's rescue is at least an assembly's worth of bytes",
d["recoveryBytes"].as_u64().unwrap_or(0) >= 80 * 1024,
s(&d)
);
}
/// A DOCUMENT WHOSE TYPE COULD NOT BE READ IS CLASSIFIED FROM ITS OWN PATH.
///
/// `GetType` is a COM call on a document being closed under memory pressure —
/// the condition this sweep exists for. When it fails, the document's own file
/// path is the strongest evidence in the room: the user's file, named by
/// SOLIDWORKS, on disk. It is NOT the circularity 0.7.5 removed, and the
/// difference is provenance — the path describes the document; the recovery
/// file's name is chosen by this sweep FROM the kind, so reading a kind back out
/// of that can only repeat what was already assumed.
#[test]
fn an_unreadable_type_falls_back_to_the_documents_own_path() {
let scratch = TempDir::new("shutdown-pathkind");
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 0, "active": "C:\\Users\\drew\\work\\rig.SLDASM",
"docs": [
// A SAVED, DIRTY ASSEMBLY whose TITLE has no extension —
// Explorer hides known types by default, so this is the common
// case and not a contrived one. Its path says what it is.
{"docType": "assembly", "path": "C:\\Users\\drew\\work\\rig.SLDASM",
"title": "rig", "saved": true, "dirty": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-pathkind-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-doctype-unreadable"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let d = rec.first().cloned().unwrap_or(json!({}));
check!(b, "the document was preserved", d["preserved"] == true, s(&r));
check!(
b,
"an unreadable type falls back to the document's own path, not to a guess",
d["docKind"] == "assembly",
s(&d)
);
check!(
b,
"...so it is written as an assembly",
d["recoveryPath"].as_str().unwrap_or("").ends_with(".SLDASM"),
s(&d)
);
check!(
b,
"...and held to the assembly floor",
d["recoveryBytes"].as_u64().unwrap_or(0) >= 80 * 1024,
s(&d)
);
}
/// AND WHEN NOTHING ANYWHERE SAYS WHAT IT IS, nothing is INVENTED — even though
/// something has to be written on the file.
///
/// Measured live 2026-09-17: `SaveAs3` to a neutral `.sldrecover` name answers
/// "format not supported" and writes nothing, so the honest name is not a name
/// this install will accept. The file is therefore named as a PART — and the
/// record says `namingFallback: "unknownKindNamedAsPart"`, the kind stays null,
/// and the verification still applies the strictest floor. The name is a label
/// SOLIDWORKS forced; the floor is the check.
#[test]
fn a_document_nothing_can_classify_is_named_as_a_part_but_never_called_one() {
let scratch = TempDir::new("shutdown-unknownkind");
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 1, "active": "untitled:a",
"docs": [
// No path, and a title with no extension: SOLIDWORKS' own
// default name for an unsaved assembly.
{"docType": "assembly", "path": "untitled:a", "title": "Assem1",
"saved": false, "dirty": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-unknownkind-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-doctype-unreadable"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let d = rec.first().cloned().unwrap_or(json!({}));
check!(b, "the kind is reported as unknown, not invented", d["docKind"].is_null(), s(&r));
check!(
b,
"...the name is a part only because SOLIDWORKS accepts no other",
d["recoveryPath"].as_str().unwrap_or("").ends_with(".SLDPRT"),
s(&d)
);
check!(
b,
"...and the record says that name was a guess",
d["namingFallback"] == "unknownKindNamedAsPart",
s(&d)
);
check!(
b,
"...and it is held to the strictest floor, not the part floor its name implies",
d["preserved"] != true || d["recoveryBytes"].as_u64().unwrap_or(0) >= 256 * 1024,
s(&d)
);
}
/// AND IF A FUTURE VERSION ACCEPTS THE NEUTRAL NAME, the honest name is used and
/// nothing is recorded as a guess. The path is kept alive and tested rather than
/// deleted, because the measurement that closed it is about THIS install and this
/// version.
#[test]
fn a_neutral_name_is_used_where_solidworks_accepts_one() {
let scratch = TempDir::new("shutdown-neutralok");
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 1, "active": "untitled:a",
"docs": [
{"docType": "assembly", "path": "untitled:a", "title": "Assem1",
"saved": false, "dirty": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-neutralok-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-doctype-unreadable"), "").unwrap();
std::fs::write(b.scratch.join("mock-neutral-extension-accepted"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
let d = r["recovered"].as_array().and_then(|a| a.first()).cloned().unwrap_or(json!({}));
check!(
b,
"the honest neutral name is used when it can be",
d["recoveryPath"].as_str().unwrap_or("").ends_with(".sldrecover"),
s(&d)
);
check!(b, "...and nothing is recorded as a guess", d["namingFallback"].is_null(), s(&d));
check!(b, "...and the kind is still unknown", d["docKind"].is_null(), s(&d));
}
/// IF SOLIDWORKS WILL NOT WRITE THE NEUTRAL NAME, the document is re-saved as a
/// part and TOLD SO — and the verification does not soften.
///
/// `SaveAs3` dispatches its writer on the extension, and `.sldrecover` matches
/// none. Whether it fails or silently falls back is not something this bridge
/// knows, so it is not assumed: the save is attempted and the failure handled. A
/// guessed NAME is tolerable now in a way it was not before 0.7.5, because
/// nothing downstream reads the kind back out of it — the unknown-kind floor
/// still applies, and the record says `unknownKindNamedAsPart` so nobody reads
/// the `.SLDPRT` as a statement of fact.
#[test]
fn a_refused_neutral_name_falls_back_to_a_part_and_says_so() {
let scratch = TempDir::new("shutdown-namefallback");
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 1, "active": "untitled:a",
"docs": [
{"docType": "assembly", "path": "untitled:a", "title": "Assem1",
"saved": false, "dirty": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-namefallback-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-doctype-unreadable"), "").unwrap();
std::fs::write(b.scratch.join("mock-saveas-rejects-unknown-extension"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let d = rec.first().cloned().unwrap_or(json!({}));
check!(b, "the document was still preserved", d["preserved"] == true, s(&r));
check!(
b,
"the name fell back to a part",
d["recoveryPath"].as_str().unwrap_or("").ends_with(".SLDPRT"),
s(&d)
);
check!(
b,
"...and says the name is a fallback, not a finding",
d["namingFallback"] == "unknownKindNamedAsPart",
s(&d)
);
// THE VERIFICATION DID NOT SOFTEN. The kind is still unknown, so the file had
// to clear the STRICTEST floor — not the 24 KB part floor its name implies.
check!(b, "the kind is still reported as unknown", d["docKind"].is_null(), s(&d));
check!(
b,
"...and it was held to the strictest floor despite the part name",
d["recoveryBytes"].as_u64().unwrap_or(0) >= 256 * 1024,
s(&d)
);
}
/// EVERY RESCUE CARRIES ITS OWN INSTRUCTION, and the one for a file SOLIDWORKS
/// cannot open is not the one for a file it can.
///
/// A `.sldrecover` file is the rescue of a document that was never saved — the
/// user's only copy — and SOLIDWORKS dispatches on extension, so `swx_open` is
/// the single instruction that cannot work for it. The general hint saying so is
/// not enough: a caller reads the entry, not the preamble, and a list where every
/// file says the same thing tells nobody which one is the problem.
#[test]
fn recover_list_tells_each_file_how_to_be_reopened() {
let scratch = TempDir::new("shutdown-howtoopen");
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 1, "active": "untitled:a",
"docs": [
// One the bridge can classify, and one nothing can.
{"docType": "part", "path": "C:\\Users\\drew\\work\\bracket.SLDPRT",
"title": "bracket.SLDPRT", "saved": true, "dirty": true},
{"docType": "assembly", "path": "untitled:a", "title": "Assem1",
"saved": false, "dirty": true}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-howtoopen-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
std::fs::write(b.scratch.join("mock-doctype-unreadable"), "").unwrap();
let swept = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "precondition: the sweep preserved both", swept["recovered"].as_array().map(|a| a.len()) == Some(2), s(&swept));
let r = b.call("swx_recover_list", json!({}));
let files: Vec<Value> = r["sweeps"]
.as_array()
.and_then(|a| a.first())
.and_then(|s| s["files"].as_array())
.cloned()
.unwrap_or_default();
check!(b, "both rescues are listed", files.len() == 2, s(&r));
// SELECTED BY WHAT IS KNOWN ABOUT THE DOCUMENT, not by how the file is
// spelled. Since SOLIDWORKS refuses a neutral extension, an unclassifiable
// document is WRITTEN as `.SLDPRT` — so a test that picked by extension would
// have quietly stopped testing anything.
let unknown = files
.iter()
.find(|f| f["kindKnown"] == false)
.cloned()
.unwrap_or(json!({}));
let known = files
.iter()
.find(|f| f["kindKnown"] == true)
.cloned()
.unwrap_or(json!({}));
check!(b, "the unclassifiable rescue is marked as such", unknown["kindKnown"] == false, s(&unknown));
check!(
b,
"...and its entry discloses that its NAME was a guess, which no file name can say",
unknown["namingFallback"] == "unknownKindNamedAsPart",
s(&unknown)
);
let how = unknown["howToOpen"].as_str().unwrap_or("").to_ascii_lowercase();
check!(b, "...and its OWN entry says to rename it", how.contains("rename"), s(&unknown));
check!(b, "...saying the extension was a guess, not an observation", how.contains("guess"), s(&unknown));
check!(
b,
"...naming the extensions to try, in order",
how.contains(".sldprt") && how.contains(".sldasm") && how.contains(".slddrw"),
s(&unknown)
);
// AND THE ORDINARY ONE IS NOT DRESSED UP AS A PROBLEM: a list where every
// entry warns is a list nobody reads.
check!(b, "the ordinary rescue is marked openable", known["kindKnown"] == true, s(&known));
let how_known = known["howToOpen"].as_str().unwrap_or("").to_ascii_lowercase();
check!(b, "...and simply says to open it", how_known.contains("swx_open") && !how_known.contains("rename"), s(&known));
}
/// A LISTING MUST TELL A RESCUE FROM A HUSK.
///
/// Files land in a sweep directory whether or not the sweep vouched for them: a
/// refused save leaves a husk, a save that returns success can write something
/// that is not a document, and a full disk leaves a truncation. In every one of
/// those the DOCUMENT WAS LEFT OPEN — the work is still in SOLIDWORKS, not in the
/// folder. `swx_recover_list` is the verb the user reads on the worst day, and
/// presenting those leftovers as rescued work is the one lie that matters here.
///
/// The verdict is a pure function of the file, so it is simply re-run per file.
#[test]
fn recover_list_never_calls_an_unverified_file_a_rescue() {
let scratch = TempDir::new("shutdown-husk");
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
let root = scratch.join("recovery").join("2026-09-12T00-00-00");
std::fs::create_dir_all(&root).unwrap();
// A GENUINE RESCUE: the marker, and a part's worth of bytes.
let mut good = vec![0x9fu8, 0xe4, 0x18, 0x9f, 0x00, 0x00, 0x00, 0x04];
good.resize(64 * 1024, b'x');
std::fs::write(root.join("bracket.SLDPRT"), &good).unwrap();
// A HUSK: plenty of bytes, no marker. This is why a document is still open.
std::fs::write(root.join("Assem1.sldrecover"), vec![0x41u8; 900_000]).unwrap();
// A TRUNCATION: the right marker, a third of a part.
let mut short = good.clone();
short.truncate(8 * 1024);
std::fs::write(root.join("shaft.SLDPRT"), &short).unwrap();
let r = b.call("swx_recover_list", json!({}));
let files = r["sweeps"][0]["files"].as_array().cloned().unwrap_or_default();
check!(b, "everything on disk is still listed", files.len() == 3, s(&r));
let by = |n: &str| files.iter().find(|f| f["name"] == n).cloned().unwrap_or(json!({}));
// A FOLDER WITH NO MANIFEST IS AN OLD ONE (or one whose record was lost), and
// the honest answer about a file that merely LOOKS right is UNKNOWN: this verb
// cannot see what the original document weighed, nor whether the extension was
// a guess, and both are what the sweep's own verdict turned on. A weaker check
// does not produce false negatives — it produces false POSITIVES, and
// `verified:true` for a file the sweep refused is the error that costs the user.
let good_f = by("bracket.SLDPRT");
check!(b, "a re-checked file is not CLAIMED as verified", good_f["verified"].is_null(), s(&good_f));
check!(b, "...and says where the answer came from", good_f["source"] == "reverified", s(&good_f));
check!(
b,
"...and is offered as unconfirmed rather than as a rescue",
good_f["howToOpen"].as_str().unwrap_or("").contains("UNCONFIRMED"),
s(&good_f)
);
for name in ["Assem1.sldrecover", "shaft.SLDPRT"] {
let f = by(name);
check!(b, "a file the sweep refused is NOT verified", f["verified"] == false, s(&f));
let how = f["howToOpen"].as_str().unwrap_or("");
check!(b, "...and is not called a rescue", how.contains("NOT A RESCUE"), s(&f));
// THE SWEEP'S OWN REASON, not a generic apology.
check!(
b,
"...and carries the reason the sweep gave",
f["verifiedReason"].as_str().unwrap_or("").len() > 20,
s(&f)
);
// AND IT IS NEVER PRESENTED AS OPENABLE — not by swx_open, and not by
// renaming, which for a husk would just produce an unopenable .SLDPRT.
check!(
b,
"...and is never offered as something to open",
!how.contains("swx_open") && !how.to_lowercase().contains("rename"),
s(&f)
);
// WHERE THE WORK IS. Without a manifest this verb cannot state that the
// sweep left the document open — only that if it is open, the work is
// there — and it is worded accordingly. The manifest path says it flatly.
check!(
b,
"...and points at SOLIDWORKS as the place the work still is",
how.contains("still open in SOLIDWORKS"),
s(&f)
);
}
// THE HEADLINE MUST NOT SAY EVERYTHING HERE IS RESCUED WORK.
let hint = r["_hint"].as_str().unwrap_or("");
check!(
b,
"the hint warns about unverified files instead of claiming they are documents",
hint.contains("verified:false") && hint.contains("NOT complete documents"),
hint.to_string()
);
}
/// THE SWEEP'S OWN RECORD IS WHAT THE LISTING REPORTS.
///
/// Everything the listing used to re-derive — what the original document weighed,
/// what kind `GetType` reported, whether the extension was a guess — was in hand
/// at save time and thrown away when the process ended. Re-deriving without them
/// is strictly weaker than the verdict it is reproducing, and a weaker check does
/// not produce false negatives; it produces FALSE POSITIVES. `verified:true` for a
/// file the sweep refused, read by the person whose sweep just failed, is the one
/// error here that costs something.
#[test]
fn the_listing_reports_the_sweeps_own_verdicts_from_its_manifest() {
let scratch = TempDir::new("shutdown-manifest");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-manifest-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
let swept = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "the user asked me to close"}));
check!(b, "precondition: the sweep ran", swept["done"] == true, s(&swept));
let r = b.call("swx_recover_list", json!({}));
let sweep = r["sweeps"].as_array().and_then(|a| a.first()).cloned().unwrap_or(json!({}));
let dir = sweep["directory"].as_str().unwrap_or("").to_string();
let files = sweep["files"].as_array().cloned().unwrap_or_default();
check!(b, "the rescues are listed", !files.is_empty(), s(&r));
// THE RECORD IS ON DISK, and it is not itself listed as a rescue.
let manifest_path = std::path::Path::new(&dir).join("manifest.json");
check!(b, "the sweep wrote its record beside the files", manifest_path.exists(), dir.clone());
check!(
b,
"...and the record is not offered to the user as recovered work",
!files.iter().any(|f| f["name"] == "manifest.json"),
s(&sweep)
);
let m: Value = serde_json::from_str(&std::fs::read_to_string(&manifest_path).unwrap_or_default())
.unwrap_or(json!({}));
check!(
b,
"the record carries the sweep's reason and every file it decided about",
m["reason"].as_str().unwrap_or("").contains("asked me to close")
&& m["files"].as_array().map(|a| !a.is_empty()).unwrap_or(false),
s(&m)
);
// THE FACTS A FILE NAME CANNOT CARRY.
let entry = m["files"].as_array().and_then(|a| a.first()).cloned().unwrap_or(json!({}));
for field in ["originalBytes", "kind", "namingFallback", "signatureOk", "closed", "verdictReason"] {
check!(b, "...including what no listing could re-derive", entry.get(field).is_some(), format!("{field}: {}", s(&entry)));
}
// AND THE LISTING ANSWERS FROM IT, saying so.
for f in &files {
check!(b, "every entry says where its verdict came from", f["source"] == "manifest", s(f));
check!(b, "...and carries a real verdict, not an unknown", f["verified"] == true, s(f));
check!(
b,
"...and says whether the document was actually closed",
f["closed"] == true,
s(f)
);
}
}
/// A SWEEP THAT IS STILL RUNNING HAS ALREADY WRITTEN DOWN WHAT IT DECIDED.
///
/// The record is rewritten after every save and every close, atomically, so a
/// bridge that dies mid-sweep leaves a manifest describing everything decided up
/// to that moment. That is the case that matters most: a sweep interrupted by a
/// crash is exactly the one whose files somebody reads later from a fresh bridge.
#[test]
fn an_interrupted_sweep_still_leaves_a_record_of_what_it_decided() {
let scratch = TempDir::new("shutdown-partialmanifest");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-partialmanifest-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
// The volume fills after the first save: the sweep stops part-way, exactly as
// an interruption would leave it.
std::fs::write(b.scratch.join("mock-dialog-after-save"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "precondition: the sweep stopped part-way", r["done"] != true, s(&r));
let listed = b.call("swx_recover_list", json!({}));
let sweep = listed["sweeps"].as_array().and_then(|a| a.first()).cloned().unwrap_or(json!({}));
let dir = sweep["directory"].as_str().unwrap_or("").to_string();
check!(
b,
"a record exists for the part that ran",
std::path::Path::new(&dir).join("manifest.json").exists(),
s(&listed)
);
let files = sweep["files"].as_array().cloned().unwrap_or_default();
check!(b, "and what was written is reported from it", files.iter().all(|f| f["source"] == "manifest"), s(&sweep));
// NOTHING WAS CLOSED — the sweep stopped — and the record says so, which is
// the difference between "your work is in this file" and "your work is still
// in SOLIDWORKS".
check!(
b,
"the record does not claim documents were closed when none were",
files.iter().all(|f| f["closed"] == false),
s(&sweep)
);
// No temporary file is left lying around pretending to be a rescue.
check!(
b,
"the atomic write leaves no half-written record behind",
!std::path::Path::new(&dir).join("manifest.json.tmp").exists(),
dir
);
}
/// TWO SWEEPS IN THE SAME SECOND DO NOT SHARE A FOLDER — OR A RECORD.
///
/// The sweep folder is named to the second. Two sweeps a fraction apart used to
/// land in the same one, and since the manifest arrived that means sharing the
/// record: the second sweep's `manifest.json` overwrites the first's, so the
/// first sweep's files sit there with nothing describing them — and if the second
/// preserved nothing, its cleanup deleted the first's manifest outright.
///
/// That is a reporting defect with a real victim (the record of why a document
/// was not preserved) and it was also the mechanism behind an intermittent
/// failure in the manifest test, about one run in twelve. It is fixed by giving
/// each sweep its own folder, not by retrying the test.
#[test]
fn two_sweeps_in_the_same_second_keep_separate_records() {
let scratch = TempDir::new("shutdown-samesecond");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-samesecond-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
// FIRST SWEEP: preserves and closes everything.
let first = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "first sweep"}));
check!(b, "the first sweep ran", first["done"] == true, s(&first));
let first_dir = first["recoveryDir"].as_str().unwrap_or("").to_string();
let first_manifest = std::path::Path::new(&first_dir).join("manifest.json");
check!(b, "it left a record", first_manifest.exists(), first_dir.clone());
let first_text = std::fs::read_to_string(&first_manifest).unwrap_or_default();
// SECOND SWEEP, IMMEDIATELY: the session is empty, so it preserves nothing —
// the case whose cleanup used to delete somebody else's record.
let second = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "second sweep"}));
let second_dir = second["recoveryDir"].as_str().unwrap_or("").to_string();
check!(
b,
"the second sweep got a folder of its own",
second_dir != first_dir,
format!("both sweeps used {first_dir}")
);
// THE FIRST SWEEP'S RECORD IS UNTOUCHED.
check!(b, "the first record still exists", first_manifest.exists(), first_dir.clone());
check!(
b,
"...and still says what the first sweep decided",
std::fs::read_to_string(&first_manifest).unwrap_or_default() == first_text,
first_dir.clone()
);
let m: Value = serde_json::from_str(&first_text).unwrap_or(json!({}));
check!(b, "...naming its own reason, not the later sweep's", m["reason"] == "first sweep", s(&m));
// AND THE LISTING STILL DESCRIBES EVERY FILE IT HOLDS.
let listed = b.call("swx_recover_list", json!({}));
let sweep = listed["sweeps"]
.as_array()
.and_then(|a| a.iter().find(|s| s["directory"] == json!(first_dir.clone())))
.cloned()
.unwrap_or(json!({}));
let files = sweep["files"].as_array().cloned().unwrap_or_default();
check!(b, "the first sweep's files are listed", !files.is_empty(), s(&listed));
check!(
b,
"...every one of them from its own sweep's record",
files.iter().all(|f| f["source"] == "manifest"),
s(&sweep)
);
}
/// THE RECORD IS WRITTEN ATOMICALLY, and nothing half-written is left behind.
///
/// A manifest is believed by the listing, so a truncated one is worse than none:
/// it would describe some files and silently omit others that are really there.
/// It is written to a temporary path and renamed.
#[test]
fn the_record_is_written_atomically() {
// The write goes via a temp path — asserted against the source, because the
// failure it prevents cannot be produced on demand from outside.
let src = std::fs::read_to_string(
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/verbs3.rs"),
)
.expect("verbs3.rs");
let body = src
.split("fn write_manifest(")
.nth(1)
.and_then(|r| r.split("\nfn ").next())
.unwrap_or("");
assert!(
body.contains("manifest.json.tmp") && body.contains("rename"),
"write_manifest must write a temporary file and RENAME it. A manifest is believed by \
swx_recover_list, so a half-written one describes some rescues and silently omits \
others. Body:\n{body}"
);
// And no temporary file survives a completed sweep.
let scratch = TempDir::new("shutdown-atomic");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-atomic-machine");
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": "closing"}));
let dir = r["recoveryDir"].as_str().unwrap_or("").to_string();
check!(
b,
"no half-written record is left behind",
!std::path::Path::new(&dir).join("manifest.json.tmp").exists(),
dir.clone()
);
check!(b, "and the real one is there", std::path::Path::new(&dir).join("manifest.json").exists(), dir);
}
/// A DIALOG IS REPORTED BY WHAT IT SAYS, NOT JUST BY ITS TITLE.
///
/// Live, 2026-09-17: the sweep stopped correctly on
/// `{"kind":"license","title":"License Manager"}` — and that was everything the
/// caller got. Read by hand afterwards, the window said:
///
/// > Failed to check 1 license(s): 3DEXPERIENCE SOLIDWORKS Professional: Unable
/// > to connect to server. The session will end after the next 5 unsuccessful
/// > attempts.
///
/// with a single button: OK. A title cannot distinguish "your licence expired"
/// from "the licence server is unreachable and this session dies in five more
/// tries" — and the second is a countdown somebody needs to see. It appeared when
/// the 3DEXPERIENCE licence heartbeat failed, coinciding with the desktop
/// dropping off the relay, and both the sweep and the quit are blocked while it
/// is up.
///
/// READING IS NOT ANSWERING. Nothing here presses anything, and `neverAnswer`
/// stays true even for a dialog whose only button is OK: whether this bridge may
/// ever press one is a policy question for the user.
#[test]
fn a_blocking_dialog_reports_its_text_and_its_buttons() {
let machine = TempDir::new("shutdown-licence-machine");
std::fs::write(
machine.join("dialog-open.json"),
json!({
"hwnd": 9400, "className": "#32770", "title": "License Manager", "visible": true,
"text": ["Failed to check 1 license(s): 3DEXPERIENCE SOLIDWORKS Professional: \
Unable to connect to server. The session will end after the next 5 \
unsuccessful attempts."],
"buttons": ["OK"]
})
.to_string(),
)
.unwrap();
let scratch = TempDir::new("shutdown-licence");
a_difficult_session(&scratch);
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": "closing"}));
check!(b, "the sweep stops", r["errorCode"] == "stopped_by_dialog", s(&r));
let d = r["stoppedByDialog"].clone();
check!(b, "the dialog is classified", d["kind"] == "license", s(&d));
check!(b, "...its contents were read", d["contentsReadable"] == true, s(&d));
check!(
b,
"...and the caller is told WHAT it says, not just its title",
d["text"][0].as_str().unwrap_or("").contains("Unable to connect to server")
&& d["text"][0].as_str().unwrap_or("").contains("next 5"),
s(&d)
);
check!(b, "...and WHICH buttons exist", d["buttons"][0] == "OK", s(&d));
check!(b, "...that the only choice is to acknowledge it", d["acknowledgeOnly"] == true, s(&d));
// AND IT IS STILL NEVER ANSWERED.
check!(b, "the bridge will not press it", d["neverAnswer"] == true, s(&d));
let after = b.call("swx_docs", json!({}));
check!(b, "nothing was closed behind it", after["count"].as_u64().unwrap_or(0) >= 4, s(&after));
// A DIALOG THAT ASKS SOMETHING IS NOT ACKNOWLEDGE-ONLY. The distinction has
// to discriminate, or it is just a field that always says yes — and this one
// exists to feed a policy decision about what may ever be pressed.
let machine2 = TempDir::new("shutdown-choice-machine");
std::fs::write(
machine2.join("dialog-open.json"),
json!({
"hwnd": 9402, "className": "#32770", "title": "Save Modified Documents",
"visible": true,
"text": ["Save changes to Part1?"],
"buttons": ["Save all to This PC", "Don't Save", "Cancel"]
})
.to_string(),
)
.unwrap();
let scratch2 = TempDir::new("shutdown-choice");
a_difficult_session(&scratch2);
let b2 = Bridge::start_in(
TempDir::borrowed(&scratch2.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine2.path())],
);
b2.call("swx_start", json!({}));
let r2 = b2.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
let d2 = r2["stoppedByDialog"].clone();
check!(
b2,
"a dialog offering real choices is NOT acknowledge-only",
d2["acknowledgeOnly"] == false,
s(&d2)
);
check!(
b2,
"...and every choice it offers is named, since each does something different to the work",
d2["buttons"].as_array().map(|a| a.len()) == Some(3),
s(&d2)
);
check!(b2, "...and it is never answered either", d2["neverAnswer"] == true, s(&d2));
}
/// AN UNREADABLE DIALOG IS UNKNOWN, NOT SILENT. The stop still happens; the
/// contents are null and say so.
#[test]
fn a_dialog_whose_controls_cannot_be_read_says_so() {
let machine = TempDir::new("shutdown-opaque-machine");
std::fs::write(
machine.join("dialog-open.json"),
json!({"hwnd": 9401, "className": "#32770", "title": "License Manager", "visible": true})
.to_string(),
)
.unwrap();
std::fs::write(machine.join("dialog-contents-unreadable"), "").unwrap();
let scratch = TempDir::new("shutdown-opaque");
a_difficult_session(&scratch);
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": "closing"}));
let d = r["stoppedByDialog"].clone();
check!(b, "the sweep still stops", r["errorCode"] == "stopped_by_dialog", s(&r));
check!(b, "the contents are UNKNOWN, not empty", d["contentsReadable"] == false, s(&d));
check!(b, "...reported as null rather than as no text", d["text"].is_null(), s(&d));
check!(b, "...and acknowledgeOnly is not guessed", d["acknowledgeOnly"].is_null(), s(&d));
check!(b, "...and it is still never answered", d["neverAnswer"] == true, s(&d));
}
/// A DOCUMENT THE SWEEP REFUSED IS RECORDED AS REFUSED — in the manifest, which
/// is the only thing `swx_recover_list` trusts afterwards.
///
/// Every other sweep test preserves everything, so `preserved` is true for every
/// record and `write_manifest`'s `verified` field is true either way. Two
/// mutations survived on that: `verified` hard-coded to true, and
/// `host_failure_entry` dropping `hostError`. Both are the fixes from passes 29
/// and 30 — the manifest being the permanent record of WHY a document was not
/// preserved — and neither had a test that could tell.
///
/// So this sweep refuses one document (the write is truncated, so the bytes are
/// not a document) and asserts the refusal survives into the record and out
/// through the listing.
#[test]
fn a_refused_document_is_recorded_as_refused_in_the_manifest() {
let scratch = TempDir::new("shutdown-refused-record");
a_difficult_session(&scratch);
let machine = a_visible_machine("shutdown-refused-record-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
b.call("swx_start", json!({}));
// A TRUNCATED WRITE: a real header, then the disk stops. The sweep refuses
// these and leaves the documents open.
std::fs::write(b.scratch.join("mock-recovery-truncated"), "").unwrap();
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
check!(b, "the sweep did not finish", r["done"] != true, s(&r));
let rec = r["recovered"].as_array().cloned().unwrap_or_default();
let refused: Vec<&Value> = rec.iter().filter(|d| d["preserved"] == false).collect();
check!(b, "at least one document was refused", !refused.is_empty(), s(&r));
// ── THE MANIFEST CARRIES THE REFUSAL, not a blanket yes.
let dir = r["recoveryDir"].as_str().unwrap_or("").to_string();
let m: Value = serde_json::from_str(
&std::fs::read_to_string(std::path::Path::new(&dir).join("manifest.json"))
.unwrap_or_default(),
)
.unwrap_or(json!({}));
let entries = m["files"].as_array().cloned().unwrap_or_default();
check!(b, "the record exists and lists files", !entries.is_empty(), s(&m));
let any_false = entries.iter().any(|e| e["verified"] == false);
check!(
b,
"a refused document is recorded verified:false — the manifest is what swx_recover_list \
trusts, so a blanket true would tell the user their only copy is intact",
any_false,
s(&m)
);
for e in entries.iter().filter(|e| e["verified"] == false) {
check!(
b,
"...with the sweep's own reason for refusing it",
e["verdictReason"].as_str().map(|r| r.len() > 20).unwrap_or(false),
s(e)
);
check!(b, "...and it was not closed", e["closed"] == false, s(e));
}
// ── AND THE LISTING REPORTS IT AS NOT A RESCUE.
let listed = b.call("swx_recover_list", json!({}));
let files = listed["sweeps"]
.as_array()
.and_then(|a| a.iter().find(|s| s["directory"] == json!(dir.clone())))
.and_then(|s| s["files"].as_array())
.cloned()
.unwrap_or_default();
let unverified: Vec<&Value> = files.iter().filter(|f| f["verified"] == false).collect();
check!(b, "the listing carries the refusal through", !unverified.is_empty(), s(&listed));
// FROM THE MANIFEST, not from a re-check. The reverified path also produces
// `verified:false` for bytes that are not a document — so without this, a
// manifest that recorded nothing at all would still satisfy the assertion
// above, and the guard being pinned here is `write_manifest`'s verdict.
check!(
b,
"...and it came from the sweep's own record, not from re-reading the file",
unverified.iter().all(|f| f["source"] == "manifest"),
s(&listed)
);
check!(
b,
"...and never calls it a rescue",
unverified.iter().all(|f| f["howToOpen"].as_str().unwrap_or("").contains("NOT A RESCUE")),
s(&listed)
);
}
/// A HOST FAILURE INSIDE THE SWEEP KEEPS ITS STRUCTURE — the other mutation that
/// survived. `host_failure_entry` exists because the sweep's records are written
/// into `manifest.json` and KEPT, so a prose-only verdict is the permanent record
/// of why a document was not preserved.
#[test]
fn a_host_failure_during_a_sweep_carries_its_structured_reason() {
let scratch = TempDir::new("shutdown-hostfail-record");
a_difficult_session(&scratch);
// A FAILURE THAT CARRIES REAL COM DETAIL. `mock-com-timeout` produces an
// error with NO data, and `Value::Null` satisfies `.is_some()` — so a test
// built on it cannot tell "the host sent nothing" from "nothing constructs
// the field", which is the mutation that survived.
std::fs::write(scratch.join("mock-preserve-host-error-with-data"), "").unwrap();
let machine = a_visible_machine("shutdown-hostfail-record-machine");
let b = Bridge::start_in(
TempDir::borrowed(&scratch.0),
&[("SWX_LAUNCH_FAKE_DIR", &machine.path())],
);
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "closing"}));
let failed = r["closeFailed"].as_array().cloned().unwrap_or_default();
check!(b, "the host failed the sweep", !failed.is_empty(), s(&r));
let e = failed.first().cloned().unwrap_or(json!({}));
check!(
b,
"the entry says the host errored",
e["why"].as_str().unwrap_or("").contains("com host errored"),
s(&e)
);
// THE DATA ITSELF, not merely a field that exists. `Value::Null.is_some()` is
// true, so asserting presence proved nothing.
check!(
b,
"...carrying the COM failure the host actually reported",
e["hostError"]["hresult"] == "0x80020005" && e["hostError"]["call"] == "SaveAs3",
s(&e)
);
check!(
b,
"...and the host's own detail",
e["detail"].as_str().unwrap_or("").contains("0x80020005"),
s(&e)
);
// AND IT REACHES THE MANIFEST, which is the permanent record of why a
// document was not preserved — a sweep that dies on the host leaves FEWER
// files than documents, and this is the only thing that can explain that.
let dir = r["recoveryDir"].as_str().unwrap_or("").to_string();
let m: Value = serde_json::from_str(
&std::fs::read_to_string(std::path::Path::new(&dir).join("manifest.json"))
.unwrap_or_default(),
)
.unwrap_or(json!({}));
let failures = m["failures"].as_array().cloned().unwrap_or_default();
check!(b, "the record names the host failure", !failures.is_empty(), s(&m));
check!(
b,
"...with its structured COM detail intact",
failures.iter().any(|f| f["hostError"]["hresult"] == "0x80020005"),
s(&m)
);
}
/// A CLEAN, SAVED DOCUMENT IS CLOSED IN PLACE — AND THE RECORD SAYS SO.
///
/// Measured live 2026-09-30: Drew's saved `R_0603_1608Metric_1.step-1.sldprt`
/// was closed by the sweep with its original untouched (32,311 bytes, same
/// mtime). That is correct — its own file IS its preservation, so the sweep
/// writes no copy — but the record had no entry for it, so `swx_recover_list`
/// could not tell a user "your file was closed at its own path, untouched",
/// which is the most reassuring thing it has to say. "Untouched" is measured,
/// not assumed: the listing compares the file now with what the sweep recorded,
/// and says when something has written to it since, or when it is gone.
#[test]
fn a_clean_document_is_recorded_as_closed_in_place_and_listed_untouched() {
let scratch = TempDir::new("shutdown-inplace");
let work = TempDir::new("shutdown-inplace-work");
let theirs = work.join("R_0603_1608Metric_1.step-1.SLDPRT");
let mut body = vec![0x9f, 0xe4, 0x18, 0x9f, 0x00, 0x00, 0x00, 0x04];
body.resize(32_311, b'x');
std::fs::write(&theirs, &body).unwrap();
let mtime_before = std::fs::metadata(&theirs).unwrap().modified().unwrap();
let theirs_s = theirs.to_string_lossy().to_string();
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true, "untitled": 1, "active": "untitled:a",
"docs": [
{"docType": "part", "path": "untitled:a", "title": "Part2",
"saved": false, "dirty": true},
{"docType": "part", "path": theirs_s,
"title": "R_0603_1608Metric_1.step-1.SLDPRT", "saved": true, "dirty": false}
],
"files": {}
})
.to_string(),
)
.unwrap();
let machine = a_visible_machine("shutdown-inplace-machine");
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, "precondition: the sweep ran and emptied the session", r["done"] == true, s(&r));
check!(
b,
"swx_close_all_safely: the user's own file is byte-identical, mtime unchanged",
std::fs::read(&theirs).ok().as_deref() == Some(&body[..])
&& std::fs::metadata(&theirs).and_then(|m| m.modified()).ok() == Some(mtime_before),
theirs_s.clone()
);
// THE RECORD.
let dir = std::path::PathBuf::from(r["recoveryDir"].as_str().unwrap_or(""));
let m: Value = serde_json::from_str(
&std::fs::read_to_string(dir.join("manifest.json")).unwrap_or_default(),
)
.unwrap_or(json!({}));
let entries = m["files"].as_array().cloned().unwrap_or_default();
let entry = entries.iter().find(|e| e["originalPath"] == json!(theirs_s)).cloned().unwrap_or(json!({}));
check!(
b,
"swx_close_all_safely: the manifest records the clean document as CLOSED IN PLACE, with no copy",
entry["closedInPlace"] == true
&& entry["name"].is_null()
&& entry["recoveryPath"].is_null()
&& entry["recoveryBytes"].is_null(),
s(&m)
);
check!(
b,
"swx_close_all_safely: ...with its original's size, and the close it was closed by",
entry["originalBytes"] == 32_311 && entry["originalModifiedMs"].is_u64() && entry["closed"] == true,
s(&entry)
);
check!(
b,
"swx_close_all_safely: ...and no claim to have verified bytes it never wrote",
entry["verified"].is_null() && entry["preserved"] == true,
s(&entry)
);
let copied = entries.iter().find(|e| e["title"] == "Part2").cloned().unwrap_or(json!({}));
check!(
b,
"swx_close_all_safely: the unsaved document is a COPY, not closed in place",
copied["closedInPlace"] == false && copied["recoveryPath"].is_string() && copied["verified"] == true,
s(&copied)
);
// THE LISTING.
let find = |l: &Value| -> Value {
l["sweeps"]
.as_array()
.and_then(|a| a.first())
.and_then(|sw| sw["closedInPlace"].as_array())
.and_then(|a| a.iter().find(|e| e["originalPath"] == json!(theirs_s)).cloned())
.unwrap_or(json!({}))
};
let l = b.call("swx_recover_list", json!({}));
let ip = find(&l);
check!(
b,
"swx_recover_list: lists the document as closed at its own path, untouched",
ip["closed"] == true
&& ip["present"] == true
&& ip["untouched"] == true
&& ip["bytesNow"] == 32_311
&& ip["howToOpen"].as_str().unwrap_or("").contains("CLOSED IN PLACE, UNTOUCHED")
&& ip["howToOpen"].as_str().unwrap_or("").contains(&theirs_s),
s(&l)
);
let files = l["sweeps"][0]["files"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_recover_list: ...and never as a file in the recovery folder",
!files.iter().any(|f| f["originalPath"] == json!(theirs_s)) && files.len() == 1,
s(&l)
);
// SOMETHING WRITES IT AFTERWARDS — same length, later mtime.
let mut rewritten = body.clone();
rewritten[100] = b'y';
std::fs::write(&theirs, &rewritten).unwrap();
std::fs::File::options()
.write(true)
.open(&theirs)
.unwrap()
.set_modified(mtime_before + std::time::Duration::from_secs(60))
.unwrap();
let ip = find(&b.call("swx_recover_list", json!({})));
check!(
b,
"swx_recover_list: a file written since the sweep is NOT called untouched, even at the same size",
ip["untouched"] == false
&& ip["bytesNow"] == 32_311
&& ip["howToOpen"].as_str().unwrap_or("").contains("CHANGED SINCE"),
s(&ip)
);
// ...and at a different size, it says what the sizes were.
std::fs::write(&theirs, [&body[..], b"more"].concat()).unwrap();
let ip = find(&b.call("swx_recover_list", json!({})));
check!(
b,
"swx_recover_list: ...and says what changed",
ip["untouched"] == false
&& ip["howToOpen"].as_str().unwrap_or("").contains("32311 byte(s) then, 32315 now"),
s(&ip)
);
// AND THEN IT IS GONE.
std::fs::remove_file(&theirs).unwrap();
let ip = find(&b.call("swx_recover_list", json!({})));
check!(
b,
"swx_recover_list: a file that is no longer at its path is reported gone, with no copy claimed",
ip["present"] == false
&& ip["untouched"].is_null()
&& ip["recoveryPath"].is_null()
&& ip["howToOpen"].as_str().unwrap_or("").contains("NOT AT ITS PATH NOW"),
s(&ip)
);
}
/// A RECORD WRITTEN BEFORE 0.9.0 has no `closedInPlace` flag, and computed
/// `closed` from the recovery path — which a document closed in place does not
/// have — so it always said false. The listing still finds such a document, and
/// reports its close as UNKNOWN rather than repeating a false that was never
/// evidence. A REFUSED document, which also has no copy, is not one of them.
#[test]
fn a_pre_0_9_record_of_a_clean_document_is_listed_with_its_close_unknown() {
let scratch = TempDir::new("shutdown-inplace-legacy");
let work = TempDir::new("shutdown-inplace-legacy-work");
let theirs = work.join("gearbox.SLDASM");
std::fs::write(&theirs, vec![b'z'; 5000]).unwrap();
let refused = work.join("refused.SLDPRT");
std::fs::write(&refused, vec![b'z'; 4000]).unwrap();
let dir = scratch.join("recovery").join("20260917-101500");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(
dir.join("manifest.json"),
s(&json!({
"manifestVersion": 1, "sweep": "20260917-101500", "reason": "old sweep",
"files": [
{"name": "", "recoveryPath": Value::Null, "title": "gearbox.SLDASM",
"originalPath": theirs.to_string_lossy(), "kind": "assembly",
"namingFallback": Value::Null, "originalBytes": Value::Null,
"recoveryBytes": Value::Null, "signatureOk": Value::Null,
"verified": true, "verdictReason": Value::Null, "preserved": true,
"closed": false, "wasDirty": false, "wasPathless": false},
{"name": "", "recoveryPath": Value::Null, "title": "refused.SLDPRT",
"originalPath": refused.to_string_lossy(), "kind": "part",
"namingFallback": Value::Null, "originalBytes": 4000,
"recoveryBytes": 0, "signatureOk": false,
"verified": false, "verdictReason": "could not be saved, so it was NOT closed",
"preserved": false, "closed": false, "wasDirty": true, "wasPathless": false}
]
})),
)
.unwrap();
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
let l = b.call("swx_recover_list", json!({}));
let ip = l["sweeps"][0]["closedInPlace"].as_array().cloned().unwrap_or_default();
check!(
b,
"swx_recover_list: finds the clean document an old record describes, and only that one",
ip.len() == 1 && ip[0]["originalPath"] == json!(theirs.to_string_lossy()),
s(&l)
);
check!(
b,
"swx_recover_list: ...with its close UNKNOWN, not the false an old record wrote",
ip[0]["closed"].is_null()
&& ip[0]["present"] == true
&& ip[0]["untouched"].is_null()
&& ip[0]["howToOpen"].as_str().unwrap_or("").contains("older than 0.9.0"),
s(&ip[0])
);
}
/// THE CRATE SESSION, MEASURED 2026-10-02 (pid 26924): 28 parts FIRST in
/// enumeration order, then the top-level assembly and four sub-assemblies. Every
/// part and sub-assembly is held open by the top level, so "still open after
/// CloseDoc" until it closes. 0.9.11's sweep walked the first `batch` documents
/// (parts), every close failed, and the no-progress stop ended it with 33 open.
/// 0.9.12 preserves the whole session first, closes in a GLOBAL order
/// (assemblies, the active one first, before parts), DEFERS what is held behind
/// an open assembly, and counts what went out with its parent as closed.
fn crate_session(tag: &str) -> (Bridge, TempDir, TempDir) {
crate_session_held(tag, false, "ASM-CRATE.SLDASM")
}
fn crate_session_held(tag: &str, top_level_held: bool, active: &str) -> (Bridge, TempDir, TempDir) {
let scratch = TempDir::new(tag);
let root = "C:\\Users\\Public\\adom-crate\\";
let mut docs: Vec<Value> = (1..=28)
.map(|i| json!({"docType": "part", "path": format!("{root}PART-{i:02}.SLDPRT"),
"title": format!("PART-{i:02}.SLDPRT"), "saved": true, "dirty": false,
"heldOpenByAssembly": true}))
.collect();
docs.push(json!({"docType": "assembly", "path": format!("{root}ASM-CRATE.SLDASM"),
"title": "ASM-CRATE.SLDASM", "saved": true, "dirty": false,
"heldOpenByAssembly": top_level_held}));
for i in 1..=4 {
docs.push(json!({"docType": "assembly", "path": format!("{root}SUB-{i}.SLDASM"),
"title": format!("SUB-{i}.SLDASM"), "saved": true, "dirty": false,
"heldOpenByAssembly": true}));
}
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({"running": true, "untitled": 0, "active": format!("{root}{active}"),
"docs": docs, "files": {}})
.to_string(),
)
.unwrap();
let machine = a_visible_machine(&format!("{tag}-m"));
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[("SWX_LAUNCH_FAKE_DIR", &machine.path())]);
b.call("swx_start", json!({}));
(b, scratch, machine)
}
#[test]
fn an_assembly_session_with_its_parts_listed_first_is_closed_completely() {
for batch in [None, Some(1u64), Some(4)] {
let (b, _s, _m) = crate_session(&format!("crate-sweep-{}", batch.map(|n| n.to_string()).unwrap_or("default".into())));
let mut args = json!({"userPresentOverride": true, "userAuthorized": true, "reason": "Drew: close the crate"});
if let Some(n) = batch {
args["batch"] = json!(n);
}
let r = b.call("swx_close_all_safely", args);
let docs = b.call("swx_docs", json!({}));
check!(
b,
&format!("batch {batch:?}: the sweep ends at 0 documents, done, nothing failed"),
r["done"] == true && docs["count"] == 0 && r["closeFailed"].as_array().map(|a| a.is_empty()).unwrap_or(false),
s(&r)
);
check!(
b,
&format!("batch {batch:?}: the components went out with their parent, after their own records"),
r["closedByParent"].as_array().map(|a| a.len() >= 28).unwrap_or(false)
&& r["recovered"].as_array().map(|a| a.len() == 33).unwrap_or(false),
s(&r)
);
}
}
/// THE USER WAS LOOKING AT A SUB-ASSEMBLY: it is the active document, so it is
/// asked first — and it is held by the top level. That close is DEFERRED, not a
/// failure and not "no progress": the top level closes next, and the session
/// ends at 0, even one close per pass.
#[test]
fn a_held_sub_assembly_asked_first_is_deferred_not_failed() {
let (b, _s, _m) = crate_session_held("crate-sub-active", false, "SUB-2.SLDASM");
let r = b.call(
"swx_close_all_safely",
json!({"userPresentOverride": true, "userAuthorized": true, "reason": "close", "batch": 1}),
);
let docs = b.call("swx_docs", json!({}));
check!(
b,
"the sub-assembly was deferred behind its parent, and the sweep ended at 0 documents",
r["done"] == true && docs["count"] == 0 && r["closeFailed"].as_array().map(|a| a.is_empty()).unwrap_or(false),
s(&r)
);
}
/// A DEFERRED CLOSE IS NOT A FAILURE — but an assembly that genuinely cannot be
/// closed leaves its components deferred, and the sweep ends honestly: no
/// progress, done:false, every held document named.
#[test]
fn components_behind_an_assembly_that_will_not_close_are_reported_not_looped() {
// The top level refuses to close as well: nothing can release anything.
let (b, _scratch, _m) = crate_session_held("crate-stuck", true, "ASM-CRATE.SLDASM");
let r = b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "close"}));
check!(
b,
"nothing could close: done false, the stop is honest, and every document is named",
r["done"] == false && r["closed"] == 0
&& r["closeFailed"].as_array().map(|a| a.len() == 33).unwrap_or(false),
s(&r)
);
}
/// swx_progress SHOWS A SWEEP WHILE IT RUNS (0.9.12): its stage and counts —
/// measured 2026-10-02 it answered `{}` while a sweep ran.
#[test]
fn a_running_sweep_is_visible_in_swx_progress() {
let (b, scratch, _m) = crate_session("crate-progress");
std::fs::write(scratch.join("mock-preserve-delay-ms"), "40").unwrap();
let frames = std::thread::scope(|sc| {
let sweeper = sc.spawn(|| {
b.call("swx_close_all_safely", json!({"userPresentOverride": true, "userAuthorized": true, "reason": "close"}))
});
let mut seen = Vec::new();
for _ in 0..60 {
std::thread::sleep(std::time::Duration::from_millis(50));
let p = b.call("swx_progress", json!({}));
if let Some(f) = p["active"].as_array().and_then(|a| a.iter().find(|f| f["phase"] == "close_all_safely").cloned()) {
seen.push(f);
}
if sweeper.is_finished() {
break;
}
}
let r = sweeper.join().unwrap();
assert!(r["done"] == true, "precondition: the sweep finished: {}", s(&r));
seen
});
check!(
b,
"swx_progress showed the sweep's stage and counts while it ran",
frames.iter().any(|f| f["detail"]["stage"] == "preserve" && f["detail"]["preserved"].is_u64()),
format!("{frames:?}")
);
}
/// THE LICENSE MANAGER AS MEASURED 2026-10-02 — ownerless, and its body a
/// DirectUIHWND task dialog: the child-control walk reads NO text. 0.9.11 said
/// `text: []` with `contentsReadable: true` — "read, and it says nothing". It is
/// unreadable, says so, still stops the sweep, and is still never answered.
#[test]
fn a_task_dialog_whose_words_cannot_be_read_says_so() {
let machine = TempDir::new("shutdown-taskdialog-machine");
std::fs::write(
machine.join("dialog-open.json"),
json!({
"hwnd": 9401, "className": "#32770", "title": "License Manager", "visible": true,
"controls": [["DirectUIHWND", 0x5600_0000u64, "", true]]
})
.to_string(),
)
.unwrap();
let scratch = TempDir::new("shutdown-taskdialog");
a_difficult_session(&scratch);
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": "closing"}));
let d = r["stoppedByDialog"].clone();
check!(
b,
"the sweep stops on it; contentsReadable is false, the text is null, and the reply says why",
r["errorCode"] == "stopped_by_dialog"
&& d["contentsReadable"] == false
&& d["text"].is_null()
&& d["contentsUnreadableWhy"].as_str().unwrap_or("").contains("DirectUIHWND")
&& d["neverAnswer"] == true,
s(&d)
);
}