use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Duration;
use crate::adapter::net::behavior::fold::{
Fold, IslandId, NodeId, ReservationFold, ReservationQuery, ReservationState, UnitId,
};
use crate::adapter::net::behavior::gang::{
acquire_gang, activate_island, Claimant, GangClaim, GangOutcome,
};
use crate::adapter::net::current_timestamp_micros;
use crate::adapter::net::identity::EntityKeypair;
fn next(state: &mut u64) -> u64 {
*state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
*state >> 33
}
fn units_of(island: IslandId) -> [UnitId; 4] {
let base = UnitId::try_from(island * 4).expect("island id too large for test unit range");
[base, base + 1, base + 2, base + 3]
}
fn holder_of(fold: &Fold<ReservationFold>, island: IslandId) -> Option<(NodeId, bool)> {
fold.query(ReservationQuery::State(island))
.first()
.and_then(|(_, s)| match s {
ReservationState::Free => None,
ReservationState::Reserved { holder, .. } => Some((*holder, false)),
ReservationState::Active { holder, .. } => Some((*holder, true)),
})
}
fn run_one_interleaving(seed: u64, gangs: usize, islands: u64) {
let fold = Arc::new(Fold::<ReservationFold>::with_sweep_interval(Duration::ZERO));
let mut rng = seed;
let gang_sets: Vec<Vec<IslandId>> = (0..gangs)
.map(|_| {
let size = 1 + (next(&mut rng) % 2) as usize;
let mut set: HashSet<IslandId> = HashSet::new();
while set.len() < size {
set.insert(next(&mut rng) % islands);
}
set.into_iter().collect()
})
.collect();
let deadline = current_timestamp_micros() + 300_000;
let handles: Vec<_> = gang_sets
.iter()
.cloned()
.map(|want| {
let fold = fold.clone();
std::thread::spawn(move || {
let kp = EntityKeypair::generate();
let node = kp.entity_id().node_id();
let mut claimant = Claimant::new(&fold, &kp, node);
let claim = GangClaim {
job: 1,
islands: want.clone(),
deadline_us: deadline,
};
let outcome = acquire_gang(
&mut claimant,
&claim,
1_000_000,
current_timestamp_micros,
|_| std::thread::sleep(Duration::from_micros(150)),
)
.expect("acquire");
if let GangOutcome::Held(held) = outcome {
for &island in &held {
activate_island(&fold, &kp, node, claimant.next_gen(), island, 1).unwrap();
}
Some((node, held))
} else {
None
}
})
})
.collect();
let results: Vec<Option<(NodeId, Vec<IslandId>)>> =
handles.into_iter().map(|h| h.join().unwrap()).collect();
for (gang_idx, result) in results.iter().enumerate() {
match result {
Some((node, held)) => {
let mut want = gang_sets[gang_idx].clone();
want.sort_unstable();
want.dedup();
let mut got = held.clone();
got.sort_unstable();
assert_eq!(got, want, "a Held gang must hold its FULL island set");
for &island in held {
assert_eq!(
holder_of(&fold, island),
Some((*node, true)),
"every island of a Held gang must be Active by it",
);
}
}
None => { }
}
}
let mut unit_owner: HashMap<UnitId, NodeId> = HashMap::new();
let mut active_islands = 0usize;
for island in 0..islands {
if let Some((holder, true)) = holder_of(&fold, island) {
active_islands += 1;
for unit in units_of(island) {
assert!(
unit_owner.insert(unit, holder).is_none(),
"unit {unit} claimed by two Active gangs (seed {seed})",
);
}
}
}
let reported: usize = results
.iter()
.filter_map(|r| r.as_ref().map(|(_, h)| h.len()))
.sum();
assert_eq!(
active_islands, reported,
"every Active island belongs to exactly one reporting gang (seed {seed})",
);
}
#[test]
fn k_gangs_over_j_islands_preserve_disjoint_active_and_all_or_none() {
for seed in [0x1111_2222, 0xDEAD_BEEF, 0x0BAD_F00D, 0x5EED_5EED] {
run_one_interleaving(seed, 6, 5);
}
}