#![cfg(target_os = "linux")] use std::sync::Arc; use data_plane::arena::{ArenaConfig, ArenaManager, NodeId}; use data_plane::byte_ring::{ByteRingSpec, RecordKind, Role, attach, install}; use data_plane::namespace::StreamIncarnation; use data_plane::stream_transport::{ LocalStreamTransport, StreamSinkRequest, StreamSourceRequest, StreamTransport, StreamTransportEvent, StreamTransportNotifier, }; use parking_lot::Mutex; use proptest::prelude::*; #[derive(Default)] struct Events(Mutex>); impl StreamTransportNotifier for Events { fn notify(&self, event: StreamTransportEvent) { self.0.lock().push(event); } } fn arena(node: u64) -> ArenaManager { ArenaManager::boot(ArenaConfig { node_id: NodeId(node), reservation_ceiling: 1 << 20, base_alignment: 64, }) .expect("arena") } #[test] fn local_transport_preserves_order_backpressure_and_eof() { let mut source_arena = arena(1); let source_handle = install( &mut source_arena, ByteRingSpec { capacity: 32, generation: 1, alignment: 64, request_id: 1, }, ) .expect("source ring"); let mut destination_arena = arena(2); let destination_handle = install( &mut destination_arena, ByteRingSpec { capacity: 16, generation: 2, alignment: 64, request_id: 2, }, ) .expect("destination ring"); let mut writer = attach(&source_arena, source_handle, Role::Producer).expect("writer"); let source_pump = attach(&source_arena, source_handle, Role::Consumer).expect("source pump"); let destination_pump = attach(&destination_arena, destination_handle, Role::Producer).expect("destination pump"); let mut reader = attach(&destination_arena, destination_handle, Role::Consumer).expect("reader"); let transport = LocalStreamTransport::new(); let incarnation = StreamIncarnation { authority_epoch: 7, revision: 11, }; let source_events = Arc::new(Events::default()); let sink_events = Arc::new(Events::default()); transport .install_sink(StreamSinkRequest { incarnation, endpoint: destination_pump, notifier: sink_events.clone(), }) .expect("install sink"); transport .install_source(StreamSourceRequest { incarnation, peer: transport.descriptor().expect("descriptor"), endpoint: source_pump, notifier: source_events.clone(), }) .expect("install source"); assert!( source_events .0 .lock() .contains(&StreamTransportEvent::Ready) ); assert!(sink_events.0.lock().contains(&StreamTransportEvent::Ready)); writer .send_record(RecordKind::Data, b"first") .expect("write first"); transport.source_progress(incarnation); writer .send_record(RecordKind::Data, b"next!") .expect("write second"); transport.source_progress(incarnation); assert_eq!( reader.recv_record().expect("read first"), Some((RecordKind::Data, b"first".to_vec())) ); assert_eq!(reader.recv_record().expect("second remains upstream"), None); transport.sink_progress(incarnation); assert_eq!( reader.recv_record().expect("read second"), Some((RecordKind::Data, b"next!".to_vec())) ); writer.send_record(RecordKind::Eof, b"").expect("write eof"); transport.source_progress(incarnation); assert_eq!( reader.recv_record().expect("read eof"), Some((RecordKind::Eof, Vec::new())) ); } #[test] fn local_transport_rejects_a_descriptor_from_another_backend() { let first = LocalStreamTransport::new(); let second = LocalStreamTransport::new(); assert_ne!( first.descriptor().expect("first descriptor"), second.descriptor().expect("second descriptor") ); } proptest! { #![proptest_config(ProptestConfig { cases: 32, max_shrink_iters: 256, ..ProptestConfig::default() })] #[test] fn randomized_payloads_preserve_exact_bytes( payload in prop::collection::vec(any::(), 0..4096), raw_capacity in 16_u8..128, chunk_seeds in prop::collection::vec(1_u8..=255, 1..32), ) { let capacity = u64::from(raw_capacity); let mut source_arena = arena(21); let source_handle = install( &mut source_arena, ByteRingSpec { capacity, generation: 1, alignment: 64, request_id: 1, }, ).expect("source ring"); let mut destination_arena = arena(22); let destination_handle = install( &mut destination_arena, ByteRingSpec { capacity, generation: 2, alignment: 64, request_id: 2, }, ).expect("destination ring"); let mut writer = attach(&source_arena, source_handle, Role::Producer).expect("writer"); let source_pump = attach(&source_arena, source_handle, Role::Consumer).expect("source pump"); let destination_pump = attach(&destination_arena, destination_handle, Role::Producer).expect("destination pump"); let mut reader = attach(&destination_arena, destination_handle, Role::Consumer).expect("reader"); let transport = LocalStreamTransport::new(); let incarnation = StreamIncarnation { authority_epoch: 3, revision: 9, }; transport.install_sink(StreamSinkRequest { incarnation, endpoint: destination_pump, notifier: Arc::new(Events::default()), }).expect("sink"); transport.install_source(StreamSourceRequest { incarnation, peer: transport.descriptor().expect("descriptor"), endpoint: source_pump, notifier: Arc::new(Events::default()), }).expect("source"); let max_chunk = capacity as usize - 5; let mut offset = 0; let mut seed_index = 0; let mut observed = Vec::new(); while offset < payload.len() { let chunk_len = usize::from(chunk_seeds[seed_index % chunk_seeds.len()]) .min(max_chunk) .min(payload.len() - offset); seed_index += 1; loop { match writer.send_record( RecordKind::Data, &payload[offset..offset + chunk_len], ) { Ok(()) => break, Err(data_plane::byte_ring::FlowError::InsufficientSpace { .. }) => { if let Some((RecordKind::Data, bytes)) = reader.recv_record().expect("drain destination") { observed.extend(bytes); transport.sink_progress(incarnation); } else { transport.source_progress(incarnation); } } Err(error) => panic!("unexpected source error: {error:?}"), } } offset += chunk_len; transport.source_progress(incarnation); } loop { match writer.send_record(RecordKind::Eof, &[]) { Ok(()) => break, Err(data_plane::byte_ring::FlowError::InsufficientSpace { .. }) => { if let Some((RecordKind::Data, bytes)) = reader.recv_record().expect("drain for eof") { observed.extend(bytes); transport.sink_progress(incarnation); } else { transport.source_progress(incarnation); } } Err(error) => panic!("unexpected eof error: {error:?}"), } } transport.source_progress(incarnation); let mut steps = 0; loop { steps += 1; prop_assert!(steps < payload.len() + 1024, "transfer made no bounded progress"); match reader.recv_record().expect("receive") { Some((RecordKind::Data, bytes)) => { observed.extend(bytes); transport.sink_progress(incarnation); } Some((RecordKind::Eof, _)) => break, Some((RecordKind::Fault, bytes)) => { return Err(TestCaseError::fail(format!( "unexpected fault record: {bytes:?}" ))); } None => transport.source_progress(incarnation), } } prop_assert_eq!(observed, payload); } }