use super::*;
use crate::fixed_point_resident_frontier::FixedPointResidentFrontierScratch;
use std::{
collections::HashSet,
sync::{
atomic::{AtomicU64, Ordering},
Arc, Mutex,
},
};
use vyre::backend::{private, BackendError, DispatchConfig, OutputBuffers, Resource};
struct FakeResidentBackend {
next: AtomicU64,
uploads: Mutex<Vec<(u64, usize)>>,
freed: Mutex<Vec<u64>>,
supported_ops: HashSet<vyre::ir::OpId>,
}
impl FakeResidentBackend {
fn new() -> Self {
Self {
next: AtomicU64::new(1),
uploads: Mutex::new(Vec::new()),
freed: Mutex::new(Vec::new()),
supported_ops: HashSet::new(),
}
}
}
impl private::Sealed for FakeResidentBackend {}
impl vyre::VyreBackend for FakeResidentBackend {
fn id(&self) -> &'static str {
"weir_test_fake_borrowed_resident_plan"
}
fn supported_ops(&self) -> &HashSet<vyre::ir::OpId> {
&self.supported_ops
}
fn dispatch(
&self,
_program: &vyre::ir::Program,
_inputs: &[Vec<u8>],
_config: &DispatchConfig,
) -> Result<Vec<Vec<u8>>, BackendError> {
unreachable!("borrowed resident plan tests do not dispatch through host bytes")
}
fn allocate_resident(&self, _byte_len: usize) -> Result<Resource, BackendError> {
Ok(Resource::Resident(
self.next.fetch_add(1, Ordering::Relaxed),
))
}
fn upload_resident(&self, resource: &Resource, bytes: &[u8]) -> Result<(), BackendError> {
let Resource::Resident(handle) = resource else {
panic!("fake backend only uploads resident resources");
};
self.uploads.lock().unwrap().push((*handle, bytes.len()));
Ok(())
}
fn upload_resident_many(&self, uploads: &[(&Resource, &[u8])]) -> Result<(), BackendError> {
for &(resource, bytes) in uploads {
self.upload_resident(resource, bytes)?;
}
Ok(())
}
fn upload_resident_at_many(
&self,
uploads: &[(&Resource, usize, &[u8])],
) -> Result<(), BackendError> {
for &(resource, offset, bytes) in uploads {
assert_eq!(
offset, 0,
"borrowed resident plan test backend only supports prefix uploads"
);
self.upload_resident(resource, bytes)?;
}
Ok(())
}
fn free_resident(&self, resource: Resource) -> Result<(), BackendError> {
let Resource::Resident(handle) = resource else {
panic!("fake backend only frees resident resources");
};
self.freed.lock().unwrap().push(handle);
Ok(())
}
}
struct FakeResidentPipeline {
seen_resident_prefix: Mutex<Vec<usize>>,
}
impl FakeResidentPipeline {
fn new() -> Self {
Self {
seen_resident_prefix: Mutex::new(Vec::new()),
}
}
}
impl private::Sealed for FakeResidentPipeline {}
impl vyre::CompiledPipeline for FakeResidentPipeline {
fn id(&self) -> &str {
"weir_test_fake_borrowed_resident_pipeline"
}
fn dispatch(
&self,
_inputs: &[Vec<u8>],
_config: &DispatchConfig,
) -> Result<Vec<Vec<u8>>, BackendError> {
unreachable!("borrowed resident plan tests do not dispatch through host bytes")
}
fn dispatch_persistent_handles(
&self,
inputs: &[Resource],
_config: &DispatchConfig,
) -> Result<OutputBuffers, BackendError> {
let resident_prefix = inputs
.iter()
.take_while(|resource| matches!(resource, Resource::Resident(_)))
.count();
self.seen_resident_prefix
.lock()
.unwrap()
.push(resident_prefix);
Ok(vec![vec![1, 0, 0, 0]])
}
}
#[test]
fn borrowed_resident_plan_uses_cached_graph_without_owning_it() {
let backend = FakeResidentBackend::new();
let graph = crate::reaching::prepare_reaching_graph(
2,
&[0, 1, 1],
&[1],
&[vyre_primitives::predicate::edge_kind::CONTROL],
)
.expect("valid graph must pack");
let mut cache = crate::fixed_point_resident_cache::FixedPointResidentGraphCache::new();
let resident = cache
.get_or_upload(&backend, &graph)
.expect("cache must upload graph once");
let pipeline = Arc::new(FakeResidentPipeline::new());
let plan = FixedPointBorrowedResidentPlan::new(resident, pipeline.clone());
let mut host_scratch = crate::fixed_point_scratch::FixedPointScratch::default();
let mut resident_frontier = FixedPointResidentFrontierScratch::default();
let result = plan
.reaching_reusing_frontier(
&backend,
&[0b01],
1,
&DispatchConfig::default(),
&mut host_scratch,
&mut resident_frontier,
)
.expect("borrowed resident plan must dispatch through cached graph");
assert_eq!(result, vec![0b01]);
assert_eq!(plan.node_count(), 2);
assert_eq!(plan.edge_count(), 1);
assert_eq!(plan.resident_resource_count(), 5);
assert_eq!(
backend.uploads.lock().unwrap().len(),
6,
"borrowed resident plan must upload four unique cached graph buffers plus frontier_in and frontier_out seed buffers"
);
assert_eq!(
pipeline.seen_resident_prefix.lock().unwrap().as_slice(),
&[7]
);
plan.clear_frontier(&backend, &mut resident_frontier)
.expect("borrowed plan must free caller frontier resources");
assert_eq!(
backend.freed.lock().unwrap().len(),
2,
"clearing a borrowed plan frontier must not free cached graph resources"
);
cache
.free_all(&backend)
.expect("cache must free resident graph resources");
assert_eq!(backend.freed.lock().unwrap().len(), 6);
}
#[test]
fn borrowed_sequence_window_rejects_plan_that_does_not_match_resident_graph() {
let backend = FakeResidentBackend::new();
let resident_graph = crate::reaching::prepare_reaching_plan(
2,
&[0, 1, 1],
&[1],
&[vyre_primitives::predicate::edge_kind::CONTROL],
)
.expect("resident plan fixture must pack");
let mismatched_plan = crate::reaching::prepare_reaching_plan(
3,
&[0, 1, 2, 2],
&[1, 2],
&[vyre_primitives::predicate::edge_kind::CONTROL; 2],
)
.expect("mismatched plan fixture must pack");
let resident = crate::fixed_point_resident::FixedPointResidentGraph::upload(
&backend,
resident_graph.graph(),
)
.expect("fake backend must upload resident graph");
let borrowed =
FixedPointBorrowedResidentPlan::new(&resident, Arc::new(FakeResidentPipeline::new()));
let mut host_scratch = crate::fixed_point_scratch::FixedPointScratch::default();
let mut resident_frontier = FixedPointResidentFrontierScratch::default();
let error = borrowed
.reaching_reusing_frontier_sequence_window(
&backend,
&mismatched_plan,
&[0b001],
4,
&mut host_scratch,
&mut resident_frontier,
)
.expect_err("sequence-window dispatch must reject mismatched prepared graph layout");
assert!(
error.contains("layout hash"),
"Fix: sequence-window resident plan mismatch errors must name the graph layout hash; got {error}"
);
assert!(
!resident_frontier.is_allocated(),
"Fix: plan/layout mismatch must fail before allocating resident frontier scratch."
);
resident
.free(&backend)
.expect("resident graph resources must free after mismatch test");
}