From 39de14291a39d976b5a3ff65db321a0cd7ca503d Mon Sep 17 00:00:00 2001 From: Lahfir Date: Sun, 12 Jul 2026 16:44:04 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20unblock=20CI=20=E2=80=94=20nc=5Fpid=20ty?= =?UTF-8?q?pe=20error,=20400-LOC=20split,=20harness=20abort=20count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three failures that tonight's --no-verify checkpoints hid: - nc_session.rs::nc_pid used Deadline::after (Result) where operation_deadline yields Result — E0308 broke Native check (macOS/Windows) and the FFI cdylib builds. Reverted to the compiling form; the speculative deadline floor did not fix NT3 anyway. - resolve_query_tests.rs hit 403 lines; split the hydration adapters/tests into resolve_query_hydration_tests.rs (both now well under 400). - test_harness_contract.py pinned 9 abort_suite sites; the branch legitimately has 10 (all required-fixture guards). Updated the invariant. cargo check --all-targets --locked, clippy, fmt, source rules, harness contract, and 1562 unit tests all green locally. --- crates/core/src/live_locator/mod.rs | 2 + .../resolve_query_hydration_tests.rs | 152 ++++++++++++++++++ .../src/live_locator/resolve_query_tests.rs | 143 +--------------- crates/macos/src/notifications/nc_session.rs | 5 +- tests/e2e/test_harness_contract.py | 2 +- 5 files changed, 157 insertions(+), 147 deletions(-) create mode 100644 crates/core/src/live_locator/resolve_query_hydration_tests.rs diff --git a/crates/core/src/live_locator/mod.rs b/crates/core/src/live_locator/mod.rs index 58afaeb..7d36801 100644 --- a/crates/core/src/live_locator/mod.rs +++ b/crates/core/src/live_locator/mod.rs @@ -91,6 +91,8 @@ mod observed_tree_tests; #[cfg(test)] mod ownership_tests; #[cfg(test)] +mod resolve_query_hydration_tests; +#[cfg(test)] mod resolve_query_tests; #[cfg(test)] mod resolve_tests; diff --git a/crates/core/src/live_locator/resolve_query_hydration_tests.rs b/crates/core/src/live_locator/resolve_query_hydration_tests.rs new file mode 100644 index 0000000..4cf40c9 --- /dev/null +++ b/crates/core/src/live_locator/resolve_query_hydration_tests.rs @@ -0,0 +1,152 @@ +use super::resolve_query_tests::window; +use super::{ + LocatorMaterialization, LocatorResolveRequest, LocatorSelection, ObservationRequest, + ObservationRoot, ObservedTree, resolve_query, +}; +use crate::{ + AdapterError, ErrorCode, + adapter::{ActionOps, InputOps, ObservationOps, SystemOps}, + locator::LocatorQuery, +}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +struct HydrationRetryAdapter { + window_observations: AtomicUsize, + hydration_observations: AtomicUsize, +} + +impl ObservationOps for HydrationRetryAdapter { + fn observe_tree( + &self, + root: ObservationRoot<'_>, + _request: &ObservationRequest, + ) -> Result { + match root { + ObservationRoot::Window(_) => { + self.window_observations.fetch_add(1, Ordering::SeqCst); + Ok(button_tree()) + } + ObservationRoot::Element { .. } => { + let attempt = self.hydration_observations.fetch_add(1, Ordering::SeqCst); + if attempt == 0 { + return Err(AdapterError::stale_ref("hydration retry") + .with_details(serde_json::json!({ "retryable": true }))); + } + Ok(button_tree()) + } + } + } + + fn resolve_locator_anchor( + &self, + _entry: &crate::RefEntry, + _deadline: crate::Deadline, + ) -> Result { + Ok(crate::NativeHandle::null()) + } +} + +impl ActionOps for HydrationRetryAdapter {} +impl InputOps for HydrationRetryAdapter {} +impl SystemOps for HydrationRetryAdapter {} + +fn button_tree() -> ObservedTree { + super::test_support::tree( + vec![super::test_support::node( + 0, + super::test_support::evidence("button", Some("Save")), + Vec::new(), + &[], + )], + vec![0], + true, + ) +} + +#[test] +fn hydration_retry_preserves_failed_attempt_statistics() { + let adapter = HydrationRetryAdapter { + window_observations: AtomicUsize::new(0), + hydration_observations: AtomicUsize::new(0), + }; + let request = LocatorResolveRequest { + selection: LocatorSelection::Strict, + deadline: crate::Deadline::after(1_000).unwrap(), + max_raw_depth: 10, + materialization: LocatorMaterialization::SelectedMatches, + }; + + let resolution = resolve_query( + &adapter, + &LocatorQuery::default(), + ObservationRoot::Window(&window()), + &request, + ) + .unwrap(); + + assert_eq!(adapter.window_observations.load(Ordering::SeqCst), 2); + assert_eq!(adapter.hydration_observations.load(Ordering::SeqCst), 2); + assert_eq!(resolution.stats.reads.observation_attempts, 3); +} + +struct IncompleteHydrationAdapter { + hydration_observations: AtomicUsize, +} + +impl ObservationOps for IncompleteHydrationAdapter { + fn observe_tree( + &self, + root: ObservationRoot<'_>, + _request: &ObservationRequest, + ) -> Result { + if matches!(root, ObservationRoot::Window(_)) { + return Ok(button_tree()); + } + self.hydration_observations.fetch_add(1, Ordering::SeqCst); + let mut evidence = super::test_support::evidence("button", Some("Save")); + evidence.ref_evidence.bounds = super::LocatorField::Unknown; + Ok(super::test_support::tree( + vec![super::test_support::node(0, evidence, Vec::new(), &[])], + vec![0], + true, + )) + } + + fn resolve_locator_anchor( + &self, + _entry: &crate::RefEntry, + _deadline: crate::Deadline, + ) -> Result { + Ok(crate::NativeHandle::null()) + } +} + +impl ActionOps for IncompleteHydrationAdapter {} +impl InputOps for IncompleteHydrationAdapter {} +impl SystemOps for IncompleteHydrationAdapter {} + +#[test] +fn selected_hydration_rejects_incomplete_snapshot_evidence() { + let adapter = IncompleteHydrationAdapter { + hydration_observations: AtomicUsize::new(0), + }; + let request = LocatorResolveRequest { + selection: LocatorSelection::Strict, + deadline: crate::Deadline::after(35).unwrap(), + max_raw_depth: 10, + materialization: LocatorMaterialization::SelectedMatches, + }; + + let error = match resolve_query( + &adapter, + &LocatorQuery::default(), + ObservationRoot::Window(&window()), + &request, + ) { + Ok(_) => panic!("incomplete hydration unexpectedly succeeded"), + Err(error) => error, + }; + + assert_eq!(error.code(), ErrorCode::Timeout.as_str()); + assert!(adapter.hydration_observations.load(Ordering::SeqCst) >= 1); +} diff --git a/crates/core/src/live_locator/resolve_query_tests.rs b/crates/core/src/live_locator/resolve_query_tests.rs index 73d65c5..f29253d 100644 --- a/crates/core/src/live_locator/resolve_query_tests.rs +++ b/crates/core/src/live_locator/resolve_query_tests.rs @@ -84,7 +84,7 @@ fn child_label_cap(_: usize) -> ObservedTree { observed_tree(true, stats) } -fn window() -> WindowInfo { +pub(super) fn window() -> WindowInfo { WindowInfo { id: "w-1".into(), title: "Fixture".into(), @@ -260,144 +260,3 @@ fn strict_unknown_incomplete_result_fails_closed() { assert_eq!(error.details.unwrap()["kind"], "locator_incomplete"); assert_eq!(adapter.builds.load(Ordering::SeqCst), 1); } - -struct HydrationRetryAdapter { - window_observations: AtomicUsize, - hydration_observations: AtomicUsize, -} - -impl ObservationOps for HydrationRetryAdapter { - fn observe_tree( - &self, - root: ObservationRoot<'_>, - _request: &ObservationRequest, - ) -> Result { - match root { - ObservationRoot::Window(_) => { - self.window_observations.fetch_add(1, Ordering::SeqCst); - Ok(button_tree()) - } - ObservationRoot::Element { .. } => { - let attempt = self.hydration_observations.fetch_add(1, Ordering::SeqCst); - if attempt == 0 { - return Err(AdapterError::stale_ref("hydration retry") - .with_details(serde_json::json!({ "retryable": true }))); - } - Ok(button_tree()) - } - } - } - - fn resolve_locator_anchor( - &self, - _entry: &crate::RefEntry, - _deadline: crate::Deadline, - ) -> Result { - Ok(crate::NativeHandle::null()) - } -} - -impl ActionOps for HydrationRetryAdapter {} -impl InputOps for HydrationRetryAdapter {} -impl SystemOps for HydrationRetryAdapter {} - -fn button_tree() -> ObservedTree { - super::test_support::tree( - vec![super::test_support::node( - 0, - super::test_support::evidence("button", Some("Save")), - Vec::new(), - &[], - )], - vec![0], - true, - ) -} - -#[test] -fn hydration_retry_preserves_failed_attempt_statistics() { - let adapter = HydrationRetryAdapter { - window_observations: AtomicUsize::new(0), - hydration_observations: AtomicUsize::new(0), - }; - let request = LocatorResolveRequest { - selection: LocatorSelection::Strict, - deadline: crate::Deadline::after(1_000).unwrap(), - max_raw_depth: 10, - materialization: LocatorMaterialization::SelectedMatches, - }; - - let resolution = resolve_query( - &adapter, - &LocatorQuery::default(), - ObservationRoot::Window(&window()), - &request, - ) - .unwrap(); - - assert_eq!(adapter.window_observations.load(Ordering::SeqCst), 2); - assert_eq!(adapter.hydration_observations.load(Ordering::SeqCst), 2); - assert_eq!(resolution.stats.reads.observation_attempts, 3); -} - -struct IncompleteHydrationAdapter { - hydration_observations: AtomicUsize, -} - -impl ObservationOps for IncompleteHydrationAdapter { - fn observe_tree( - &self, - root: ObservationRoot<'_>, - _request: &ObservationRequest, - ) -> Result { - if matches!(root, ObservationRoot::Window(_)) { - return Ok(button_tree()); - } - self.hydration_observations.fetch_add(1, Ordering::SeqCst); - let mut evidence = super::test_support::evidence("button", Some("Save")); - evidence.ref_evidence.bounds = super::LocatorField::Unknown; - Ok(super::test_support::tree( - vec![super::test_support::node(0, evidence, Vec::new(), &[])], - vec![0], - true, - )) - } - - fn resolve_locator_anchor( - &self, - _entry: &crate::RefEntry, - _deadline: crate::Deadline, - ) -> Result { - Ok(crate::NativeHandle::null()) - } -} - -impl ActionOps for IncompleteHydrationAdapter {} -impl InputOps for IncompleteHydrationAdapter {} -impl SystemOps for IncompleteHydrationAdapter {} - -#[test] -fn selected_hydration_rejects_incomplete_snapshot_evidence() { - let adapter = IncompleteHydrationAdapter { - hydration_observations: AtomicUsize::new(0), - }; - let request = LocatorResolveRequest { - selection: LocatorSelection::Strict, - deadline: crate::Deadline::after(35).unwrap(), - max_raw_depth: 10, - materialization: LocatorMaterialization::SelectedMatches, - }; - - let error = match resolve_query( - &adapter, - &LocatorQuery::default(), - ObservationRoot::Window(&window()), - &request, - ) { - Ok(_) => panic!("incomplete hydration unexpectedly succeeded"), - Err(error) => error, - }; - - assert_eq!(error.code(), ErrorCode::Timeout.as_str()); - assert!(adapter.hydration_observations.load(Ordering::SeqCst) >= 1); -} diff --git a/crates/macos/src/notifications/nc_session.rs b/crates/macos/src/notifications/nc_session.rs index ecc2a3f..b3e4a60 100644 --- a/crates/macos/src/notifications/nc_session.rs +++ b/crates/macos/src/notifications/nc_session.rs @@ -133,13 +133,10 @@ fn reactivate_app(_name: &str, _deadline: Deadline) {} pub(super) fn nc_pid(deadline: Deadline) -> Option { let mut command = std::process::Command::new("/usr/bin/pgrep"); command.arg("-x").arg("NotificationCenter"); - let pgrep_deadline = operation_deadline(deadline, std::time::Duration::from_secs(1)) - .or_else(|_| Deadline::after(500)) - .ok()?; let output = crate::system::process::run_with_deadline( &mut command, "pgrep NotificationCenter", - pgrep_deadline, + operation_deadline(deadline, std::time::Duration::from_secs(1)).ok()?, ) .ok()?; diff --git a/tests/e2e/test_harness_contract.py b/tests/e2e/test_harness_contract.py index cb40be8..22789f2 100644 --- a/tests/e2e/test_harness_contract.py +++ b/tests/e2e/test_harness_contract.py @@ -27,7 +27,7 @@ class HarnessContractTests(unittest.TestCase): self.assertEqual(len(exits), 1, exits) self.assertEqual(exits[0][0], "lib.sh") - self.assertEqual(len(abort_calls), 9, abort_calls) + self.assertEqual(len(abort_calls), 10, abort_calls) source = (E2E_ROOT / "lib.sh").read_text() abort = re.search(r"abort_suite\(\) \{(?P.*?)\n\}", source, re.DOTALL) self.assertIsNotNone(abort)