#![allow(missing_docs)]
use std::sync::mpsc::Sender;
use std::sync::Mutex;
use std::time::Duration;
use wireshift_core::backend::{
Backend, BackendCompletion, BackendFactory, BackendKind, BackendSubmission, BoxedBackend,
CancellationHandle,
};
use wireshift_core::error::{Error, Result};
use wireshift_core::op::CompletionPayload;
use wireshift_core::ops::Nop;
use wireshift_core::strategy::BatchDrain;
use wireshift_core::{Ring, RingConfig};
struct OneThenDisconnectBackend {
tx: Mutex<Option<Sender<BackendCompletion>>>,
}
impl Backend for OneThenDisconnectBackend {
fn kind(&self) -> BackendKind {
BackendKind::Fallback
}
fn submit(&self, submission: BackendSubmission) -> Result<()> {
let mut guard = self.tx.lock().expect("mock completion tx mutex");
if let Some(tx) = guard.take() {
tx.send(BackendCompletion {
id: submission.id,
result: Ok(CompletionPayload::Unit),
})
.expect("send mock completion");
}
Ok(())
}
fn cancel(&self, _cancellation: CancellationHandle) -> Result<()> {
Ok(())
}
fn shutdown(&self) -> Result<()> {
Ok(())
}
}
#[derive(Default, Clone)]
struct OneThenDisconnectFactory;
impl BackendFactory for OneThenDisconnectFactory {
fn create(
&self,
_config: &RingConfig,
completion_tx: Sender<BackendCompletion>,
) -> Result<BoxedBackend> {
Ok(Box::new(OneThenDisconnectBackend {
tx: Mutex::new(Some(completion_tx)),
}))
}
}
#[test]
fn batch_drain_surfaces_non_timeout_error_after_the_first_completion() {
let ring: Ring<OneThenDisconnectFactory> = Ring::new(RingConfig::default()).expect("ring");
let _req = ring.submit(Nop).expect("submit nop");
let drain = BatchDrain::new(ring.clone(), 8);
let results = drain.drain(Some(Duration::from_millis(100)));
assert_eq!(
results.len(),
2,
"expected the ok completion plus the surfaced disconnect error, got {results:?}"
);
assert!(
results[0].is_ok(),
"first completion should route ok, got {:?}",
results[0]
);
let err = results[1]
.as_ref()
.expect_err("second poll must surface the disconnect error, not break quietly");
assert!(
matches!(err, Error::Completion { .. }),
"expected a non-timeout completion error, got {err:?}"
);
assert!(
err.to_string().contains("disconnected"),
"error should name the channel disconnect: {err}"
);
}