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.9.5 source: adversary passes 42+43 folded (gated by default, log bound to session, a window's words count); manifest…
b63f99e
20d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
//! ADVERSARIAL REVIEW OF 0.9.4 — pass forty-three: quit::judge_window.
//!
//! `judge_window`, `dialog_census` and `abwin::classify` are not cfg(windows):
//! they are exercised here directly. The LiveOs code that MEASURES a window
//! (GW_OWNER, GWL_STYLE/EXSTYLE, the child-control walk) is cfg(windows) and was
//! READ, NOT RUN.
//!
//! WHAT CHECKS OUT
//! * Ownerless windows fail closed. LiveOs reads the DIRECT owner
//! (`GetWindow(GW_OWNER)`); no owner -> `owner_enabled: None` -> "could not be
//! read" -> BLOCKING. A zero owner style is likewise treated as a failed read.
//! * The direct owner is the right one for modality: a modal disables whatever
//! owns it. A modal owned by another popup disables that popup and is judged
//! blocking; a tool window owned by a still-enabled popup is a notification,
//! while the popup's own modal (if any) is judged separately.
//! * Hidden Button-class children COUNT (`has_button_class_child` ignores
//! visibility). That is the safe direction; its cost is that a notice whose
//! template carries hidden Button controls blocks every quit again — the
//! pre-0.9.4 symptom. Visible-only would trade that for a modal whose buttons
//! are created hidden and shown late. I would keep hidden-counts.
//! * A real modal appearing between the precondition census and the rung is
//! caught: verbs.rs re-censuses after every rung (1755-1764), judged, until
//! the process is gone, a blocking dialog appears, or the wait ends.
//! * Consequence of a FALSE notification, by verb: a sweep saves every document
//! before closing anything, so "never discard" holds whatever a dialog was;
//! swx_stop requires zero unsaved documents (P2, only `force` waives it), so
//! ExitApp past a misread modeless question loses the QUESTION, not a
//! document — and with unsaved work SOLIDWORKS raises its own MODAL save
//! prompt, which disables the frame and is caught by the post-rung census.
//! That bound is why finding 1 below is sev-2 and not sev-1.
//! * The localised License Manager: if its title is not English, `classify`
//! misses it — and it is still blocking, because it has a Button-class OK.
use swx_bridge::launchos::{DialogContents, WindowInfo, WS_EX_TOOLWINDOW};
use swx_bridge::quit::judge_window;
/// The window measured on 2026-10-01: #32770, style 0x94000044, exstyle 0x90080,
/// 301x67, owned by the enabled main frame.
fn measured_notice(title: &str) -> WindowInfo {
WindowInfo {
hwnd: 331_370,
class_name: "#32770".into(),
title: title.into(),
visible: true,
owner: Some(1001),
owner_enabled: Some(true),
style: Some(0x9400_0044),
exstyle: Some(0x0009_0080),
rect: Some([100, 100, 401, 167]),
}
}
fn statics(text: &[&str]) -> DialogContents {
DialogContents {
text: text.iter().map(|s| s.to_string()).collect(),
controls: text.iter().map(|_| ("Static".to_string(), true)).collect(),
..Default::default()
}
}
/// SEVERITY 2 — THE WINDOW'S OWN WORDS ARE READ AND THEN IGNORED.
///
/// `judge_window` classifies by TITLE, then modality, then Button class. The
/// text it read is never consulted. SOLIDWORKS titles its prompts "SOLIDWORKS"
/// and puts the question in the body, and it draws many controls itself — the
/// very notice this release was built around draws its text in Static controls.
/// A modeless tool window, owner enabled, whose body says "Do you want to save
/// changes to Part1.SLDPRT?" and whose Save / Don't Save / Cancel are owner-drawn
/// (Static, or a custom class) is judged a NOTIFICATION: reported, never a reason
/// to stop a quit, a sweep or an import.
///
/// Bounded (see the module note): the sweep saves first; swx_stop needs zero
/// unsaved documents and SOLIDWORKS re-asks modally. But a notification is
/// "nothing anyone needs to decide", and this one is a decision. Run `classify`
/// over the read text too, and treat more than one clickable-looking caption
/// (Save/Don't Save/Cancel/Yes/No/Retry…) as something on offer, whatever its
/// class.
#[test]
fn a_custom_drawn_save_prompt_is_not_judged_a_notification() {
let w = measured_notice("SOLIDWORKS");
let c = statics(&[
"Do you want to save changes to Part1.SLDPRT?",
"Save",
"Don't Save",
"Cancel",
]);
assert_eq!(w.exstyle.unwrap() & WS_EX_TOOLWINDOW, WS_EX_TOOLWINDOW, "precondition: a tool window");
let (blocking, kind, why) = judge_window(&w, Some(&c));
assert!(
blocking,
"a window that SAYS \"Do you want to save changes to Part1.SLDPRT?\" and offers \
Save / Don't Save / Cancel — drawn as Static, the way this release's own measured notice \
draws its text — was judged {kind:?}, non-blocking: {why}. judge_window reads the text \
and never looks at it. Classify the text as well as the title."
);
}
/// SEVERITY 3 — A TITLED 3DEXPERIENCE NOTIFICATION REGRESSES TO THE LIVE SYMPTOM.
///
/// The title check runs BEFORE the measured modality, and `classify` maps any
/// title containing "3dexperience" (also "licen", "activat") to `license`, a
/// known blocking kind — pinned that way in the abwin cases table. The notice
/// measured today was title-less, so 0.9.4 works on it. The same notice with a
/// title — "3DEXPERIENCE Platform", "Connected to …" — and identical evidence
/// (tool window, owner enabled, Static only) is BLOCKING by title and stops every
/// quit, which is exactly the symptom 0.9.4 fixed. Safe direction, availability
/// cost. For `license` the title adds nothing a real licence dialog needs (the
/// License Manager has a Button-class OK and blocks on that); let measured
/// notification evidence beat a title-only guess for the kinds that are not a
/// save, recovery or import prompt.
#[test]
fn a_titled_3dexperience_notice_with_notification_evidence_is_a_notification() {
let w = measured_notice("3DEXPERIENCE Platform");
let c = statics(&["Connected to OI000616940"]);
let (blocking, kind, why) = judge_window(&w, Some(&c));
assert!(
!blocking,
"the measured notice with a TITLE added — same tool window, enabled owner, Static-only \
controls — is judged {kind:?} and BLOCKING by its title alone ({why}), so it stops \
every quit: the 0.9.4 live symptom, back. Measured modality should beat a title-only \
guess for kinds that are not save/recovery/import prompts."
);
}
/// PIN — the rows of the decision that must not move. Passes today.
#[test]
fn judge_window_fails_closed_on_every_unread_or_modal_fact() {
let notice = measured_notice("");
let text = statics(&["Title", "Description"]);
assert!(!judge_window(¬ice, Some(&text)).0, "the measured notice is a notification");
let mut ownerless = notice.clone();
ownerless.owner = None;
ownerless.owner_enabled = None;
assert!(judge_window(&ownerless, Some(&text)).0, "ownerless: owner state unread -> blocking");
let mut modal = notice.clone();
modal.owner_enabled = Some(false);
assert!(judge_window(&modal, Some(&text)).0, "owner disabled -> blocking");
assert!(judge_window(¬ice, None).0, "controls unread -> blocking");
let mut hidden_button = text.clone();
hidden_button.controls.push(("Button".into(), false));
assert!(judge_window(¬ice, Some(&hidden_button)).0, "a HIDDEN Button-class child still blocks");
let mut not_tool = notice.clone();
not_tool.exstyle = Some(0x0009_0000);
assert!(judge_window(¬_tool, Some(&text)).0, "not a tool window -> blocking");
let mut unread_ex = notice.clone();
unread_ex.exstyle = None;
assert!(judge_window(&unread_ex, Some(&text)).0, "extended style unread -> blocking");
assert!(judge_window(&measured_notice("Save Modified Documents"), Some(&text)).0, "a save title blocks");
}