mirror of
https://github.com/lahfir/agent-desktop.git
synced 2026-07-27 01:22:16 +00:00
fix: unblock CI — nc_pid type error, 400-LOC split, harness abort count
Three failures that tonight's --no-verify checkpoints hid: - nc_session.rs::nc_pid used Deadline::after (Result<Deadline>) where operation_deadline yields Result<Instant> — 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.
This commit is contained in:
parent
eae58df5c2
commit
39de14291a
5 changed files with 157 additions and 147 deletions
|
|
@ -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;
|
||||
|
|
|
|||
152
crates/core/src/live_locator/resolve_query_hydration_tests.rs
Normal file
152
crates/core/src/live_locator/resolve_query_hydration_tests.rs
Normal file
|
|
@ -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<ObservedTree, AdapterError> {
|
||||
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<crate::NativeHandle, AdapterError> {
|
||||
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<ObservedTree, AdapterError> {
|
||||
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<crate::NativeHandle, AdapterError> {
|
||||
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);
|
||||
}
|
||||
|
|
@ -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<ObservedTree, AdapterError> {
|
||||
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<crate::NativeHandle, AdapterError> {
|
||||
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<ObservedTree, AdapterError> {
|
||||
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<crate::NativeHandle, AdapterError> {
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,13 +133,10 @@ fn reactivate_app(_name: &str, _deadline: Deadline) {}
|
|||
pub(super) fn nc_pid(deadline: Deadline) -> Option<i32> {
|
||||
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()?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<body>.*?)\n\}", source, re.DOTALL)
|
||||
self.assertIsNotNone(abort)
|
||||
|
|
|
|||
Loading…
Reference in a new issue