tests: rework the actually useful tests to live in the integration crate
Rust CI / cargo fmt (push) Successful in 17s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 34s
Rust CI / cargo clippy (push) Successful in 35s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Failing after 8s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Failing after 10s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Successful in 1m14s
Rust CI / cargo doc (push) Successful in 23s
Fuzz Smoke / cargo fuzz smoke (push) Failing after 29s
Supply chain / cargo-deny advisories (push) Successful in 9s
Supply chain / cargo-deny bans licenses sources (push) Successful in 5s
Supply chain / actionlint (push) Successful in 3s
Netem Interop / netem interop (push) Successful in 1m11s
Rust CI / cargo fmt (push) Successful in 17s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 34s
Rust CI / cargo clippy (push) Successful in 35s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Failing after 8s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Failing after 10s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Successful in 1m14s
Rust CI / cargo doc (push) Successful in 23s
Fuzz Smoke / cargo fuzz smoke (push) Failing after 29s
Supply chain / cargo-deny advisories (push) Successful in 9s
Supply chain / cargo-deny bans licenses sources (push) Successful in 5s
Supply chain / actionlint (push) Successful in 3s
Netem Interop / netem interop (push) Successful in 1m11s
This commit is contained in:
101 files changed
+3636
-15159
No files matched your search
@@ -177,74 +177,6 @@ pub struct ManagedClientHandle {
|
||||
max_live_target_generations: usize,
|
||||
status: watch::Receiver<Arc<ManagedStatus>>,
|
||||
events: broadcast::WeakSender<ManagedEvent>,
|
||||
#[cfg(test)]
|
||||
update_submission_hook: Option<Arc<UpdateSubmissionTestHook>>,
|
||||
}
|
||||
|
||||
// Pauses between admission and enqueue to exercise stop/seal linearization.
|
||||
#[cfg(test)]
|
||||
#[derive(Default)]
|
||||
struct UpdateSubmissionTestHook {
|
||||
state: std::sync::Mutex<UpdateSubmissionTestState>,
|
||||
arrived: std::sync::Condvar,
|
||||
released: std::sync::Condvar,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(Default)]
|
||||
struct UpdateSubmissionTestState {
|
||||
armed: bool,
|
||||
arrived: bool,
|
||||
released: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl UpdateSubmissionTestHook {
|
||||
fn arm(&self) {
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.armed = true;
|
||||
}
|
||||
|
||||
fn pause_after_admission(&self) {
|
||||
let mut state = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
if !state.armed {
|
||||
return;
|
||||
}
|
||||
state.arrived = true;
|
||||
self.arrived.notify_all();
|
||||
while !state.released {
|
||||
state = self
|
||||
.released
|
||||
.wait(state)
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_until_arrived(&self) {
|
||||
let state = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let (state, timeout) = self
|
||||
.arrived
|
||||
.wait_timeout_while(state, Duration::from_secs(2), |state| !state.arrived)
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
assert!(state.arrived && !timeout.timed_out());
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
let mut state = self
|
||||
.state
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
state.released = true;
|
||||
self.released.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ManagedClientHandle {
|
||||
@@ -306,11 +238,6 @@ impl ManagedClientHandle {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(hook) = &self.update_submission_hook {
|
||||
hook.pause_after_admission();
|
||||
}
|
||||
|
||||
// Observing `Open` only permits attempting submission. A successful
|
||||
// `try_send` transfers command ownership to the driver. A concurrent
|
||||
// stop may move admission to `Stopping` before this send, allowing the
|
||||
@@ -403,9 +330,6 @@ impl Future for ManagedStopReceipt {
|
||||
type ConnectFuture =
|
||||
Pin<Box<dyn Future<Output = Result<AsyncClient, ClientError>> + Send + 'static>>;
|
||||
type WakeFuture = Pin<Box<dyn Future<Output = Option<watch::Receiver<()>>> + Send + 'static>>;
|
||||
// Snapshots at publication verify status is visible before the corresponding event.
|
||||
#[cfg(test)]
|
||||
type EventObservations = Arc<std::sync::Mutex<Vec<(ManagedEvent, Arc<ManagedStatus>)>>>;
|
||||
|
||||
fn arm_wake(mut receiver: watch::Receiver<()>) -> WakeFuture {
|
||||
Box::pin(async move {
|
||||
@@ -688,6 +612,8 @@ enum OpenSessionFailureCleanup {
|
||||
/// The sole authoritative zero-or-many target driver.
|
||||
#[must_use = "ManagedClientTask must be awaited or deliberately dropped"]
|
||||
pub struct ManagedClientTask {
|
||||
#[cfg(test)]
|
||||
timeout_inspections: std::cell::RefCell<Vec<usize>>,
|
||||
state: DriverState,
|
||||
lifecycle: ManagedLifecycle,
|
||||
config: ManagedClientConfig,
|
||||
@@ -710,20 +636,6 @@ pub struct ManagedClientTask {
|
||||
final_outcome: Option<Arc<ManagedOutcome>>,
|
||||
next_generation: u64,
|
||||
applied_command_sequence: u64,
|
||||
#[cfg(test)]
|
||||
event_observations: Option<EventObservations>,
|
||||
#[cfg(test)]
|
||||
drain_test_hook: DrainTestHook,
|
||||
// Counts work that cannot be observed through lossy presentation events.
|
||||
#[cfg(test)]
|
||||
timeout_inspections: usize,
|
||||
}
|
||||
|
||||
// Holds queued replies past the drain deadline to exercise the final receive budget.
|
||||
#[cfg(test)]
|
||||
#[derive(Default)]
|
||||
struct DrainTestHook {
|
||||
defer_work_until_deadline: bool,
|
||||
}
|
||||
|
||||
impl ManagedClientTask {
|
||||
@@ -734,13 +646,6 @@ impl ManagedClientTask {
|
||||
}
|
||||
|
||||
fn publish_event(&self, event: ManagedEvent) {
|
||||
#[cfg(test)]
|
||||
if let Some(observations) = &self.event_observations {
|
||||
observations
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.push((event.clone(), Arc::clone(&self.resources().status.borrow())));
|
||||
}
|
||||
if let Some(events) = &self.resources().events {
|
||||
let _ = events.send(event);
|
||||
}
|
||||
@@ -1376,22 +1281,6 @@ impl ManagedClientTask {
|
||||
mut cleanup_failure,
|
||||
mut post_deadline_receives_remaining,
|
||||
} => {
|
||||
#[cfg(test)]
|
||||
let defer_work = self.drain_test_hook.defer_work_until_deadline && now < deadline;
|
||||
#[cfg(not(test))]
|
||||
let defer_work = false;
|
||||
if defer_work {
|
||||
self.targets[index].state = TargetState::Draining {
|
||||
client,
|
||||
drain_started_at,
|
||||
deadline,
|
||||
primary_end,
|
||||
cleanup_failure,
|
||||
post_deadline_receives_remaining,
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
if client
|
||||
.next_probe_timeout_deadline()
|
||||
.is_some_and(|timeout| timeout <= now)
|
||||
@@ -1899,10 +1788,6 @@ impl ManagedClientTask {
|
||||
}
|
||||
let index = self.timeout_cursor;
|
||||
self.timeout_cursor = (self.timeout_cursor + 1) % target_len;
|
||||
#[cfg(test)]
|
||||
{
|
||||
self.timeout_inspections += 1;
|
||||
}
|
||||
if self.target_has_due_timeout(index, now) {
|
||||
let generation = self.targets[index].instance.generation;
|
||||
let step = self.poll_target_timeout(index, now);
|
||||
@@ -1950,6 +1835,8 @@ impl ManagedClientTask {
|
||||
}
|
||||
|
||||
fn target_has_due_timeout(&self, index: usize, now: Instant) -> bool {
|
||||
#[cfg(test)]
|
||||
self.timeout_inspections.borrow_mut().push(index);
|
||||
match &self.targets[index].state {
|
||||
TargetState::Active { client } => {
|
||||
self.state != DriverState::Stopping
|
||||
@@ -1958,20 +1845,9 @@ impl ManagedClientTask {
|
||||
.next_probe_timeout_deadline()
|
||||
.is_some_and(|deadline| deadline <= now)
|
||||
}
|
||||
TargetState::Draining {
|
||||
client,
|
||||
deadline: _deadline,
|
||||
..
|
||||
} => {
|
||||
#[cfg(test)]
|
||||
let defer_work = self.drain_test_hook.defer_work_until_deadline && now < *_deadline;
|
||||
#[cfg(not(test))]
|
||||
let defer_work = false;
|
||||
!defer_work
|
||||
&& client
|
||||
.next_probe_timeout_deadline()
|
||||
.is_some_and(|timeout| timeout <= now)
|
||||
}
|
||||
TargetState::Draining { client, .. } => client
|
||||
.next_probe_timeout_deadline()
|
||||
.is_some_and(|timeout| timeout <= now),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -2076,17 +1952,9 @@ impl ManagedClientTask {
|
||||
TargetState::Draining {
|
||||
client, deadline, ..
|
||||
} => {
|
||||
#[cfg(test)]
|
||||
let defer_work = self.drain_test_hook.defer_work_until_deadline;
|
||||
#[cfg(not(test))]
|
||||
let defer_work = false;
|
||||
non_send_deadline = non_send_deadline
|
||||
.into_iter()
|
||||
.chain(
|
||||
(!defer_work)
|
||||
.then(|| client.next_probe_timeout_deadline())
|
||||
.flatten(),
|
||||
)
|
||||
.chain(client.next_probe_timeout_deadline())
|
||||
.chain(Some(*deadline))
|
||||
.min();
|
||||
}
|
||||
@@ -2454,6 +2322,8 @@ fn build_task(
|
||||
stop: Arc::clone(&stop),
|
||||
};
|
||||
let task = ManagedClientTask {
|
||||
#[cfg(test)]
|
||||
timeout_inspections: std::cell::RefCell::new(Vec::new()),
|
||||
state: DriverState::NotStarted,
|
||||
lifecycle: ManagedLifecycle::NotStarted,
|
||||
config,
|
||||
@@ -2476,12 +2346,6 @@ fn build_task(
|
||||
final_outcome: None,
|
||||
next_generation,
|
||||
applied_command_sequence: 0,
|
||||
#[cfg(test)]
|
||||
event_observations: None,
|
||||
#[cfg(test)]
|
||||
drain_test_hook: DrainTestHook::default(),
|
||||
#[cfg(test)]
|
||||
timeout_inspections: 0,
|
||||
};
|
||||
let handle = ManagedClientHandle {
|
||||
stop,
|
||||
@@ -2489,8 +2353,6 @@ fn build_task(
|
||||
max_live_target_generations,
|
||||
status: status_receiver,
|
||||
events: weak_events,
|
||||
#[cfg(test)]
|
||||
update_submission_hook: None,
|
||||
};
|
||||
Ok((task, handle))
|
||||
}
|
||||
@@ -2533,5 +2395,39 @@ fn close_timeout_failure() -> ManagedTargetFailure {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "tests.rs"]
|
||||
mod tests;
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Poll latency through public events cannot distinguish an O(n) discovery
|
||||
// scan from a bounded scan reliably. This tiny inspection trace measures
|
||||
// work directly, including empty targets, and checks eventual coverage
|
||||
// without asserting a cursor value or a particular visitation order.
|
||||
#[test]
|
||||
fn timeout_discovery_has_bounded_work_and_eventually_visits_every_target() {
|
||||
let count = TIMEOUT_WORK_BUDGET * 2 + 1;
|
||||
let targets = (0..count)
|
||||
.map(|i| ManagedTargetConfig::new(i.to_string(), "127.0.0.1:2112"))
|
||||
.collect();
|
||||
let (mut task, _handle) = ManagedClient::task(
|
||||
ManagedClientConfig {
|
||||
max_live_target_generations: count,
|
||||
..ManagedClientConfig::default()
|
||||
},
|
||||
targets,
|
||||
)
|
||||
.unwrap();
|
||||
let mut visited = HashSet::new();
|
||||
for _ in 0..count.div_ceil(TIMEOUT_WORK_BUDGET) {
|
||||
task.timeout_inspections.borrow_mut().clear();
|
||||
task.poll_timeout_pass(Instant::now());
|
||||
let inspected = task.timeout_inspections.borrow();
|
||||
assert!(inspected.len() <= TIMEOUT_WORK_BUDGET);
|
||||
visited.extend(inspected.iter().copied());
|
||||
}
|
||||
assert_eq!(
|
||||
visited.len(),
|
||||
count,
|
||||
"timeout discovery must reach all targets"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user