app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
Drew Owens
0.6.9 source: launch resilience, honest counts, ownership persistence, drain, quit ladder — 21 adversary passes, 303 te…
cf16621
1mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
//! ADVERSARIAL REVIEW OF 0.5.5 — pass nine, the drain's close key.
//!
//! The 0.5.5 walk is right where it was wrong: `walk_documents` is bounded by a
//! count AND a time box, reports `walkTruncatedByTime`, and re-reads each path as
//! it passes so a victim is identified from live evidence. `documents()` now
//! REFUSES above the cap and every caller propagates that error with `?`, so no
//! consumer turns "too many to marshal" into "none open". `importTempsElsewhere`
//! is explicitly documented as a count over the SCANNED window and is reported
//! beside `documentsScanned`. The latency fault is charged per document at the
//! points that pay a real round trip.
//!
//! But the victim is chosen BY PATH and then closed BY TITLE:
//!
//! ```text
//! for (title, path) in victims {
//! if sw.call("CloseDoc", &[&Var::bstr(&title)]).is_ok() {
//! ```
//!
//! `ISldWorks::CloseDoc` resolves a document by NAME, and SOLIDWORKS titles are
//! not unique across paths. Drew's session right now holds saved
//! `…\validation\First.step-1.SLDPRT` files whose titles are shared by import
//! temporaries. Closing by title asks SOLIDWORKS to pick, and `CloseDoc` does not
//! prompt.
//!
//! THE MOCK CANNOT SHOW THIS. Its drain removes by `d.path` — a unique internal
//! key — so it always closes exactly the right document, and the 254 green tests
//! are not evidence about the close key. The test below therefore asserts the
//! GUARD that is missing rather than pretending the mock reproduces the fault.
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()
}
/// Drew's live session, reduced to the two documents that matter: a SAVED file
/// he owns and an import temporary that carries the SAME TITLE.
fn session_with_a_title_collision(scratch: &TempDir) {
let temp_root = r"C:\Users\drew\AppData\Local\Temp";
std::fs::write(
scratch.join("mock-solidworks-open-docs.json"),
json!({
"running": true,
"untitled": 2,
"active": "C:\\Users\\drew\\validation\\First.step-1.SLDPRT",
"docs": [
// The user's own saved document. NOT a temp, and its loss is
// unrecoverable.
{
"docType": "part",
"path": "C:\\Users\\drew\\validation\\First.step-1.SLDPRT",
"title": "First.step-1.SLDPRT",
"saved": true,
"importTemp": false
},
// A leaked conversion temporary that happens to carry the same
// title — which is exactly what repeated imports of one fixture
// produce.
{
"docType": "part",
"path": "untitled:import:1:First.step-1.SLDPRT",
"title": "First.step-1.SLDPRT",
"saved": false,
"importTemp": true,
"tempPath": format!("{temp_root}\\swx107688\\IC~~\\First.step-1.SLDPRT")
}
],
"files": {}
})
.to_string(),
)
.unwrap();
}
/// The drain must not close a victim whose title is shared by another open
/// document — because the only close key it has is that title.
///
/// Nothing in the response even lets a caller SEE the collision: no
/// `titleCollisions`, no per-victim "this title is unique among open documents",
/// no post-close verification that the document still at that path is the one
/// that went. On a real backend that is the difference between draining a
/// temporary and discarding Drew's validation part with no save prompt.
#[test]
fn the_drain_refuses_a_victim_whose_title_another_open_document_shares() {
let scratch = TempDir::new("adv9-scratch");
session_with_a_title_collision(&scratch);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({"reason": "draining"}));
let before = b.call("swx_docs", json!({}));
let titled = |v: &Value| -> usize {
v["docs"]
.as_array()
.map(|a| {
a.iter()
.filter(|d| d.get("title").and_then(|t| t.as_str()) == Some("First.step-1.SLDPRT"))
.count()
})
.unwrap_or(0)
};
assert_eq!(
titled(&before),
2,
"precondition: two open documents share the title, one saved and one a temp: {}",
s(&before)
);
let r = b.call("swx_close_import_temps", json!({"reason": "draining leaked temps"}));
let text = s(&r).to_lowercase();
assert!(
text.contains("titlecollision")
|| text.contains("ambiguous")
|| text.contains("shares its title")
|| r["closed"].as_u64() == Some(0),
"the drain closed a victim by TITLE while a second open document — a SAVED file the user \
owns at C:\\Users\\drew\\validation\\First.step-1.SLDPRT — carries the same title, and \
the response says nothing about it. CloseDoc resolves by name and does not prompt, so on \
the real backend SOLIDWORKS chooses which of the two goes. The victim is identified by \
PATH; it has to be closed by something that identifies it too.\n{}",
s(&r)
);
}
/// And the invariant itself, pinned. This PASSES today only because the mock
/// removes by its own unique key rather than by title — so it is a regression pin
/// for whenever the mock is made faithful to `CloseDoc`, not evidence that the
/// real backend is safe.
#[test]
fn a_saved_document_sharing_a_temps_title_survives_the_drain() {
let scratch = TempDir::new("adv9-scratch2");
session_with_a_title_collision(&scratch);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({"reason": "draining"}));
b.call("swx_close_import_temps", json!({"reason": "draining leaked temps"}));
let after = b.call("swx_docs", json!({}));
let saved_alive = after["docs"]
.as_array()
.map(|a| {
a.iter().any(|d| {
d.get("path").and_then(|p| p.as_str())
== Some("C:\\Users\\drew\\validation\\First.step-1.SLDPRT")
})
})
.unwrap_or(false);
assert!(
saved_alive,
"the user's SAVED document at …\\validation\\First.step-1.SLDPRT is gone after a drain \
that was supposed to touch only conversion temporaries.\n{}",
s(&after)
);
}
/// `walk_documents` stops on the first `GetNext` that does not answer:
///
/// ```text
/// let next = doc.obj("GetNext", &[]).ok().flatten();
/// ```
///
/// A document closed by the user (or by SOLIDWORKS) mid-walk invalidates the
/// iteration, `GetNext` fails, `.ok().flatten()` makes it `None`, and the walk
/// ENDS — with `walkTruncatedByTime:false` and therefore `done:true`. It never
/// closes a wrong document (each path is re-read as it is passed), but a walk
/// that was cut short is reported as one that finished. On a flooded session that
/// is the difference between "drained" and "stopped after four of 2144".
///
/// The fix is a comparison the verb already has both sides of: `documentsScanned`
/// against `documentCountBefore`.
#[test]
fn a_walk_that_ended_early_is_not_reported_as_complete() {
let scratch = TempDir::new("adv9-scratch3");
session_with_a_title_collision(&scratch);
let b = Bridge::start_in(TempDir::borrowed(&scratch.0), &[]);
b.call("swx_start", json!({"reason": "draining"}));
let r = b.call("swx_close_import_temps", json!({"reason": "draining leaked temps"}));
let text = s(&r).to_lowercase();
assert!(
text.contains("walkcomplete") || text.contains("walkincomplete"),
"the response reports `walkTruncatedByTime` and `documentsScanned` but has no field that \
says whether the walk actually REACHED THE END of the session. GetNext failing on an \
invalidated iteration is indistinguishable in this output from a walk that finished, and \
`done` is computed as if it finished.\n{}",
s(&r)
);
}